<!DOCTYPE html>
<title>ServiceWorker FetchEvent for sandboxed iframe.</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
var lastCallbackId = 0;
var callbacks = {};
function doTest(frame, type) {
  return new Promise(function(resolve) {
    var id = ++lastCallbackId;
    callbacks[id] = resolve;
    frame.contentWindow.postMessage({id: id, type: type}, '*');
  });
}

// Asks the service worker for data about requests and clients seen. The
// worker posts a message back with |data| where:
// |data.requests|: the requests the worker received FetchEvents for
// |data.clients|: the URLs of all the worker's clients
// The worker clears its data after responding.
function getResultsFromWorker(worker) {
  return new Promise(resolve => {
    let channel = new MessageChannel();
    channel.port1.onmessage = msg => {
      resolve(msg.data);
    };
    worker.postMessage({port: channel.port2}, [channel.port2]);
  });
}

window.onmessage = function (e) {
  message = e.data;
  var id = message['id'];
  var callback = callbacks[id];
  delete callbacks[id];
  callback(message['result']);
};

const SCOPE = 'resources/sandboxed-iframe-fetch-event-iframe.py';
const SCRIPT = 'resources/sandboxed-iframe-fetch-event-worker.js';
const expected_base_url = new URL(SCOPE, location.href);
// Service worker controlling |SCOPE|.
let worker;
// A normal iframe.
// This should be controlled by a service worker.
let normal_frame;
// An iframe created by <iframe sandbox='allow-scripts'>.
// This should NOT be controlled by a service worker.
let sandboxed_frame;
// An iframe created by <iframe sandbox='allow-scripts allow-same-origin'>.
// This should be controlled by a service worker.
let sandboxed_same_origin_frame;
// An iframe whose response header has
// 'Content-Security-Policy: allow-scripts'.
// This should NOT be controlled by a service worker.
let sandboxed_frame_by_header;
// An iframe whose response header has
// 'Content-Security-Policy: allow-scripts allow-same-origin'.
// This should be controlled by a service worker.
let sandboxed_same_origin_frame_by_header;

promise_test(t => {
  return service_worker_unregister_and_register(t, SCRIPT, SCOPE)
    .then(function(registration) {
      add_completion_callback(() => registration.unregister());
      worker = registration.installing;
      return wait_for_state(t, registration.installing, 'activated');
    });
}, 'Prepare a service worker.');

promise_test(t => {
  return with_iframe(SCOPE + '?iframe')
    .then(f => {
      normal_frame = f;
      add_completion_callback(() => f.remove());
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1);
      assert_equals(requests[0], expected_base_url + '?iframe');
      assert_true(data.clients.includes(expected_base_url + '?iframe'));
    });
}, 'Prepare a normal iframe.');

promise_test(t => {
  return with_sandboxed_iframe(SCOPE + '?sandboxed-iframe', 'allow-scripts')
    .then(f => {
      sandboxed_frame = f;
      add_completion_callback(() => f.remove());
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0);
      assert_false(data.clients.includes(expected_base_url +
                                         '?sandboxed-iframe'));
    });
}, 'Prepare an iframe sandboxed by <iframe sandbox="allow-scripts">.');

promise_test(t => {
  return with_sandboxed_iframe(SCOPE + '?sandboxed-iframe-same-origin',
                               'allow-scripts allow-same-origin')
    .then(f => {
      sandboxed_same_origin_frame = f;
      add_completion_callback(() => f.remove());
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1);
      assert_equals(requests[0],
                    expected_base_url + '?sandboxed-iframe-same-origin');
      assert_true(data.clients.includes(
        expected_base_url + '?sandboxed-iframe-same-origin'));
    })
}, 'Prepare an iframe sandboxed by ' +
   '<iframe sandbox="allow-scripts allow-same-origin">.');

promise_test(t => {
  const iframe_full_url = expected_base_url + '?sandbox=allow-scripts&' +
                          'sandboxed-frame-by-header';
  return with_iframe(iframe_full_url)
    .then(f => {
      sandboxed_frame_by_header = f;
      add_completion_callback(() => f.remove());
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1,
                    'Service worker should provide the response');
      assert_equals(requests[0], iframe_full_url);
      assert_false(data.clients.includes(iframe_full_url),
                   'Service worker should NOT control the sandboxed page');
    });
}, 'Prepare an iframe sandboxed by CSP HTTP header with allow-scripts.');

promise_test(t => {
  const iframe_full_url =
    expected_base_url + '?sandbox=allow-scripts%20allow-same-origin&' +
    'sandboxed-iframe-same-origin-by-header';
  return with_iframe(iframe_full_url)
    .then(f => {
      sandboxed_same_origin_frame_by_header = f;
      add_completion_callback(() => f.remove());
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1);
      assert_equals(requests[0], iframe_full_url);
      assert_true(data.clients.includes(iframe_full_url));
    })
}, 'Prepare an iframe sandboxed by CSP HTTP header with allow-scripts and ' +
   'allow-same-origin.');

promise_test(t => {
  let frame = normal_frame;
  return doTest(frame, 'fetch')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1,
                    'The fetch request should be handled by SW.');
      assert_equals(requests[0], frame.src + '&test=fetch');
    });
}, 'Fetch request from a normal iframe');

promise_test(t => {
  let frame = normal_frame;
  return doTest(frame, 'fetch-from-worker')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1,
                    'The fetch request should be handled by SW.');
      assert_equals(requests[0], frame.src + '&test=fetch-from-worker');
    });
}, 'Fetch request from a worker in a normal iframe');

promise_test(t => {
  let frame = normal_frame;
  return doTest(frame, 'iframe')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1,
                    'The request should be handled by SW.');
      assert_equals(requests[0], frame.src + '&test=iframe');
      assert_true(data.clients.includes(frame.src + '&test=iframe'));

    });
}, 'Request for an iframe in the normal iframe');

promise_test(t => {
  let frame = normal_frame;
  return doTest(frame, 'sandboxed-iframe')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0,
                    'The request should NOT be handled by SW.');
      assert_false(data.clients.includes(
        frame.src + '&test=sandboxed-iframe'));
    });
}, 'Request for an sandboxed iframe with allow-scripts flag in the normal ' +
   'iframe');

promise_test(t => {
  let frame = normal_frame;
  return doTest(frame, 'sandboxed-iframe-same-origin')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1,
                    'The request should be handled by SW.');
      assert_equals(requests[0],
                    frame.src + '&test=sandboxed-iframe-same-origin');
      assert_true(data.clients.includes(
        frame.src + '&test=sandboxed-iframe-same-origin'));
    });
}, 'Request for an sandboxed iframe with allow-scripts and ' +
   'allow-same-origin flag in the normal iframe');

promise_test(t => {
  let frame = sandboxed_frame;
  return doTest(frame, 'fetch')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0,
                    'The fetch request should NOT be handled by SW.');
    });
}, 'Fetch request from iframe sandboxed by an attribute with allow-scripts ' +
   'flag');

promise_test(t => {
  let frame = sandboxed_frame;
  return doTest(frame, 'fetch-from-worker')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0,
                    'The fetch request should NOT be handled by SW.');
    });
}, 'Fetch request from a worker in iframe sandboxed by an attribute with ' +
   'allow-scripts flag');

promise_test(t => {
  let frame = sandboxed_frame;
  return doTest(frame, 'iframe')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0,
                    'The request should NOT be handled by SW.');
      assert_false(data.clients.includes(frame.src + '&test=iframe'));
    });
}, 'Request for an iframe in the iframe sandboxed by an attribute with ' +
   'allow-scripts flag');

promise_test(t => {
  let frame = sandboxed_frame;
  return doTest(frame, 'sandboxed-iframe')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0,
                    'The request should NOT be handled by SW.');
      assert_false(data.clients.includes(
        frame.src + '&test=sandboxed-iframe'));
    });
}, 'Request for an sandboxed iframe with allow-scripts flag in the iframe ' +
   'sandboxed by an attribute with allow-scripts flag');

promise_test(t => {
  let frame = sandboxed_frame;
  return doTest(frame, 'sandboxed-iframe-same-origin')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0,
                    'The request should NOT be handled by SW.');
      assert_false(data.clients.includes(
        frame.src + '&test=sandboxed-iframe-same-origin'));
    });
}, 'Request for an sandboxed iframe with allow-scripts and ' +
   'allow-same-origin flag in the iframe sandboxed by an attribute with ' +
   'allow-scripts flag');

promise_test(t => {
  let frame = sandboxed_same_origin_frame;
  return doTest(frame, 'fetch')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1,
                    'The fetch request should be handled by SW.');
      assert_equals(requests[0], frame.src + '&test=fetch');
    });
}, 'Fetch request from iframe sandboxed by an attribute with allow-scripts ' +
   'and allow-same-origin flag');

promise_test(t => {
  let frame = sandboxed_same_origin_frame;
  return doTest(frame, 'fetch-from-worker')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1,
                    'The fetch request should be handled by SW.');
      assert_equals(requests[0],
                    frame.src + '&test=fetch-from-worker');
    });
}, 'Fetch request from a worker in iframe sandboxed by an attribute with ' +
   'allow-scripts and allow-same-origin flag');

promise_test(t => {
  let frame = sandboxed_same_origin_frame;
  return doTest(frame, 'iframe')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1,
                    'The request should be handled by SW.');
      assert_equals(requests[0], frame.src + '&test=iframe');
      assert_true(data.clients.includes(frame.src + '&test=iframe'));
    });
}, 'Request for an iframe in the iframe sandboxed by an attribute with ' +
   'allow-scripts and allow-same-origin flag');

promise_test(t => {
  let frame = sandboxed_same_origin_frame;
  return doTest(frame, 'sandboxed-iframe')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0,
                    'The request should NOT be handled by SW.');
      assert_false(data.clients.includes(
        frame.src + '&test=sandboxed-iframe'));
    });
}, 'Request for an sandboxed iframe with allow-scripts flag in the iframe ' +
   'sandboxed by attribute with allow-scripts and allow-same-origin flag');

promise_test(t => {
  let frame = sandboxed_same_origin_frame;
  return doTest(frame, 'sandboxed-iframe-same-origin')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1,
                    'The request should be handled by SW.');
      assert_equals(requests[0],
                    frame.src + '&test=sandboxed-iframe-same-origin');
      assert_true(data.clients.includes(
        frame.src + '&test=sandboxed-iframe-same-origin'));
    });
}, 'Request for an sandboxed iframe with allow-scripts and ' +
   'allow-same-origin flag in the iframe sandboxed by attribute with ' +
   'allow-scripts and allow-same-origin flag');

promise_test(t => {
  let frame = sandboxed_frame_by_header;
  return doTest(frame, 'fetch')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0,
                    'The request should NOT be handled by SW.');
    });
}, 'Fetch request from iframe sandboxed by CSP HTTP header with ' +
   'allow-scripts flag');

promise_test(t => {
  let frame = sandboxed_frame_by_header;
  return doTest(frame, 'iframe')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0,
                    'The request should NOT be handled by SW.');
      assert_false(data.clients.includes(frame.src + '&test=iframe'));
    });
}, 'Request for an iframe in the iframe sandboxed by CSP HTTP header with ' +
   'allow-scripts flag');

promise_test(t => {
  let frame = sandboxed_frame_by_header;
  return doTest(frame, 'sandboxed-iframe')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0,
                    'The request should NOT be handled by SW.');
      assert_false(data.clients.includes(
        frame.src + '&test=sandboxed-iframe'));
    });
}, 'Request for an sandboxed iframe with allow-scripts flag in the iframe ' +
   'sandboxed by CSP HTTP header with allow-scripts flag');

promise_test(t => {
  let frame = sandboxed_frame_by_header;
  return doTest(frame, 'sandboxed-iframe-same-origin')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0,
                    'The request should NOT be handled by SW.');
      assert_false(data.clients.includes(
        frame.src + '&test=sandboxed-iframe-same-origin'));
    });
}, 'Request for an sandboxed iframe with allow-scripts and ' +
   'allow-same-origin flag in the iframe sandboxed by CSP HTTP header with ' +
   'allow-scripts flag');

promise_test(t => {
  let frame = sandboxed_same_origin_frame_by_header;
  return doTest(frame, 'fetch')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1,
                    'The request should be handled by SW.');
      assert_equals(requests[0], frame.src + '&test=fetch');
    });
}, 'Fetch request from iframe sandboxed by CSP HTTP header with ' +
   'allow-scripts and allow-same-origin flag');

promise_test(t => {
  let frame = sandboxed_same_origin_frame_by_header;
  return doTest(frame, 'iframe')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1,
                    'The request should be handled by SW.');
      assert_equals(requests[0], frame.src + '&test=iframe');
      assert_true(data.clients.includes(frame.src + '&test=iframe'));
    });
}, 'Request for an iframe in the iframe sandboxed by CSP HTTP header with ' +
   'allow-scripts and allow-same-origin flag');

promise_test(t => {
  let frame = sandboxed_same_origin_frame_by_header;
  return doTest(frame, 'sandboxed-iframe')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      assert_equals(data.requests.length, 0,
                    'The request should NOT be handled by SW.');
      assert_false(
        data.clients.includes(frame.src + '&test=sandboxed-iframe'));
    });
}, 'Request for an sandboxed iframe with allow-scripts flag in the ' +
   'iframe sandboxed by CSP HTTP header with allow-scripts and ' +
   'allow-same-origin flag');

promise_test(t => {
  let frame = sandboxed_same_origin_frame_by_header;
  return doTest(frame, 'sandboxed-iframe-same-origin')
    .then(result => {
      assert_equals(result, 'done');
      return getResultsFromWorker(worker);
    })
    .then(data => {
      let requests = data.requests;
      assert_equals(requests.length, 1,
                    'The request should be handled by SW.');
      assert_equals(requests[0],
                    frame.src + '&test=sandboxed-iframe-same-origin');
      assert_true(data.clients.includes(
        frame.src + '&test=sandboxed-iframe-same-origin'));
    });
}, 'Request for an sandboxed iframe with allow-scripts and ' +
   'allow-same-origin flag in the iframe sandboxed by CSP HTTP header with ' +
   'allow-scripts and allow-same-origin flag');
</script>
</body>
