<!DOCTYPE html>
<title>Service Worker: Clients.matchAll with includeUncontrolled</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
function test_matchall(service_worker, expected, query_options) {
  expected.sort((a, b) => a[2] > b[2] ? 1 : -1);
  return new Promise((resolve, reject) => {
    const channel = new MessageChannel();
    channel.port1.onmessage = e => {
      const data = e.data.filter(info => {
        return info[2].indexOf('clients-matchall') > -1;
      });
      data.sort((a, b) => a[2] > b[2] ? 1 : -1);
      assert_equals(data.length, expected.length);
      for (let i = 0; i < data.length; i++)
        assert_array_equals(data[i], expected[i]);
      resolve();
    };
    service_worker.postMessage({port:channel.port2, options:query_options},
                               [channel.port2]);
  });
}

// Run clients.matchAll without and with includeUncontrolled=true.
// (We want to run the two tests sequentially in the same promise_test
// so that we can use the same set of iframes without intefering each other.
promise_test(async t => {
  // |base_url| is out-of-scope.
  const base_url = 'resources/blank.html?clients-matchall';
  const scope = base_url + '-includeUncontrolled';

  const registration =
      await service_worker_unregister_and_register(
          t, 'resources/clients-matchall-worker.js', scope);
  t.add_cleanup(() => service_worker_unregister(t, scope));
  const service_worker = registration.installing;
  await wait_for_state(t, service_worker, 'activated');

  // Creates 3 iframes, 2 for in-scope and 1 for out-of-scope.
  let frames = [];
  frames.push(await with_iframe(base_url));
  frames.push(await with_iframe(scope + '#1'));
  frames.push(await with_iframe(scope + '#2'));

  // Make sure we have focus for '#2' frame and its parent window.
  frames[2].focus();
  frames[2].contentWindow.focus();

  const expected_without_include_uncontrolled = [
    // visibilityState, focused, url, type, frameType
    ['visible', false, new URL(scope + '#1', location).toString(), 'window', 'nested'],
    ['visible', true, new URL(scope + '#2', location).toString(), 'window', 'nested']
  ];
  const expected_with_include_uncontrolled = [
    // visibilityState, focused, url, type, frameType
    ['visible', true, location.href, 'window', 'top-level'],
    ['visible', false, new URL(scope + '#1', location).toString(), 'window', 'nested'],
    ['visible', true, new URL(scope + '#2', location).toString(), 'window', 'nested'],
    ['visible', false, new URL(base_url, location).toString(), 'window', 'nested']
  ];

  await test_matchall(service_worker, expected_without_include_uncontrolled);
  await test_matchall(service_worker, expected_with_include_uncontrolled,
                      { includeUncontrolled: true });
}, 'Verify matchAll() with windows respect includeUncontrolled');

// TODO: Add tests for clients.matchAll for dedicated workers.

async function create_shared_worker(script_url) {
  const shared_worker = new SharedWorker(script_url);
  const msgEvent = await new Promise(r => shared_worker.port.onmessage = r);
  assert_equals(msgEvent.data, 'started');
  return shared_worker;
}

// Run clients.matchAll for shared workers without and with
// includeUncontrolled=true.
promise_test(async t => {
  const script_url = 'resources/clients-matchall-client-types-shared-worker.js';
  const uncontrolled_script_url =
      new URL(script_url + '?uncontrolled', location).toString();
  const controlled_script_url =
      new URL(script_url + '?controlled', location).toString();

  // Start a shared worker that is not controlled by a service worker.
  const uncontrolled_shared_worker =
      await create_shared_worker(uncontrolled_script_url);

  // Register a service worker.
  const registration =
      await service_worker_unregister_and_register(
          t, 'resources/clients-matchall-worker.js', script_url);
  t.add_cleanup(() => service_worker_unregister(t, script_url));
  const service_worker = registration.installing;
  await wait_for_state(t, service_worker, 'activated');

  // Start another shared worker controlled by the service worker.
  await create_shared_worker(controlled_script_url);

  const expected_without_include_uncontrolled = [
    // visibilityState, focused, url, type, frameType
    [undefined, undefined, controlled_script_url, 'sharedworker', 'none'],
  ];
  const expected_with_include_uncontrolled = [
    // visibilityState, focused, url, type, frameType
    [undefined, undefined, controlled_script_url, 'sharedworker', 'none'],
    [undefined, undefined, uncontrolled_script_url, 'sharedworker', 'none'],
  ];

  await test_matchall(service_worker, expected_without_include_uncontrolled,
                      { type: 'sharedworker' });
  await test_matchall(service_worker, expected_with_include_uncontrolled,
                      { includeUncontrolled: true, type: 'sharedworker' });
}, 'Verify matchAll() with shared workers respect includeUncontrolled');
</script>
