<!DOCTYPE html>
<title>ServiceWorkerGlobalScope: ExtendableMessageEvent</title>
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
<script src='../resources/test-helpers.sub.js'></script>
<script src='./resources/extendable-message-event-utils.js'></script>
<script>
promise_test(function(t) {
    var script = 'resources/extendable-message-event-worker.js';
    var scope = 'resources/scope/extendable-message-event-from-toplevel';
    var registration;

    return service_worker_unregister_and_register(t, script, scope)
      .then(function(r) {
          registration = r;
          add_completion_callback(function() { registration.unregister(); });
          return wait_for_state(t, registration.installing, 'activated');
        })
      .then(function() {
          var saw_message = new Promise(function(resolve) {
              navigator.serviceWorker.onmessage =
                  function(event) { resolve(event.data); }
            });
          var channel = new MessageChannel;
          registration.active.postMessage('', [channel.port1]);
          return saw_message;
        })
      .then(function(results) {
          var expected = {
            constructor: { name: 'ExtendableMessageEvent' },
            origin: location.origin,
            lastEventId: '',
            source: {
                constructor: { name: 'WindowClient' },
                frameType: 'top-level',
                url: location.href,
                visibilityState: 'visible',
                focused: true
            },
            ports: [ { constructor: { name: 'MessagePort' } } ]
          };
          ExtendableMessageEventUtils.assert_equals(results, expected);
        });
  }, 'Post an extendable message from a top-level client');

promise_test(function(t) {
    var script = 'resources/extendable-message-event-worker.js';
    var scope = 'resources/scope/extendable-message-event-from-nested';
    var frame;

    return service_worker_unregister_and_register(t, script, scope)
      .then(function(registration) {
          add_completion_callback(function() { registration.unregister(); });
          return wait_for_state(t, registration.installing, 'activated');
        })
      .then(function() { return with_iframe(scope); })
      .then(function(f) {
          frame = f;
          add_completion_callback(function() { frame.remove(); });
          var saw_message = new Promise(function(resolve) {
              frame.contentWindow.navigator.serviceWorker.onmessage =
                  function(event) { resolve(event.data); }
            });
          f.contentWindow.navigator.serviceWorker.controller.postMessage('');
          return saw_message;
        })
      .then(function(results) {
          var expected = {
              constructor: { name: 'ExtendableMessageEvent' },
              origin: location.origin,
              lastEventId: '',
              source: {
                  constructor: { name: 'WindowClient' },
                  url: frame.contentWindow.location.href,
                  frameType: 'nested',
                  visibilityState: 'visible',
                  focused: false
              },
              ports: []
            };
          ExtendableMessageEventUtils.assert_equals(results, expected);
        });
  }, 'Post an extendable message from a nested client');

promise_test(function(t) {
    var script = 'resources/extendable-message-event-loopback-worker.js';
    var scope = 'resources/scope/extendable-message-event-loopback';
    var registration;

    return service_worker_unregister_and_register(t, script, scope)
      .then(function(r) {
          registration = r;
          add_completion_callback(function() { registration.unregister(); });
          return wait_for_state(t, registration.installing, 'activated');
        })
      .then(function() {
          var results = [];
          var saw_message = new Promise(function(resolve) {
              navigator.serviceWorker.onmessage = function(event) {
                switch (event.data.type) {
                  case 'record':
                    results.push(event.data.results);
                    break;
                  case 'finish':
                    resolve(results);
                    break;
                }
              };
            });
          registration.active.postMessage({type: 'start'});
          return saw_message;
        })
      .then(function(results) {
          assert_equals(results.length, 2);

          var expected_trial_1 = {
              constructor: { name: 'ExtendableMessageEvent' },
              origin: location.origin,
              lastEventId: '',
              source: {
                  constructor: { name: 'ServiceWorker' },
                  scriptURL: normalizeURL(script),
                  state: 'activated'
              },
              ports: []
          };
          assert_equals(results[0].trial, 1);
          ExtendableMessageEventUtils.assert_equals(
              results[0].event, expected_trial_1
          );

          var expected_trial_2 = {
              constructor: { name: 'ExtendableMessageEvent' },
              origin: location.origin,
              lastEventId: '',
              source: {
                  constructor: { name: 'ServiceWorker' },
                  scriptURL: normalizeURL(script),
                  state: 'activated'
              },
              ports: [],
          };
          assert_equals(results[1].trial, 2);
          ExtendableMessageEventUtils.assert_equals(
              results[1].event, expected_trial_2
          );
        });
  }, 'Post loopback extendable messages');

promise_test(function(t) {
    var script1 = 'resources/extendable-message-event-ping-worker.js';
    var script2 = 'resources/extendable-message-event-pong-worker.js';
    var scope = 'resources/scope/extendable-message-event-pingpong';
    var registration;

    return service_worker_unregister_and_register(t, script1, scope)
      .then(function(r) {
          registration = r;
          add_completion_callback(function() { registration.unregister(); });
          return wait_for_state(t, registration.installing, 'activated');
        })
      .then(function() {
          // A controlled frame is necessary for keeping a waiting worker.
          return with_iframe(scope);
        })
      .then(function(frame) {
          add_completion_callback(function() { frame.remove(); });
          return navigator.serviceWorker.register(script2, {scope: scope});
        })
      .then(function(r) {
          return wait_for_state(t, r.installing, 'installed');
        })
      .then(function() {
          var results = [];
          var saw_message = new Promise(function(resolve) {
              navigator.serviceWorker.onmessage = function(event) {
                switch (event.data.type) {
                  case 'record':
                    results.push(event.data.results);
                    break;
                  case 'finish':
                    resolve(results);
                    break;
                }
              };
            });
          registration.active.postMessage({type: 'start'});
          return saw_message;
        })
      .then(function(results) {
          assert_equals(results.length, 2);

          var expected_ping = {
              constructor: { name: 'ExtendableMessageEvent' },
              origin: location.origin,
              lastEventId: '',
              source: {
                  constructor: { name: 'ServiceWorker' },
                  scriptURL: normalizeURL(script1),
                  state: 'activated'
              },
              ports: []
          };
          assert_equals(results[0].pingOrPong, 'ping');
          ExtendableMessageEventUtils.assert_equals(
              results[0].event, expected_ping
          );

          var expected_pong = {
              constructor: { name: 'ExtendableMessageEvent' },
              origin: location.origin,
              lastEventId: '',
              source: {
                  constructor: { name: 'ServiceWorker' },
                  scriptURL: normalizeURL(script2),
                  state: 'installed'
              },
              ports: []
          };
          assert_equals(results[1].pingOrPong, 'pong');
          ExtendableMessageEventUtils.assert_equals(
              results[1].event, expected_pong
          );
        });
  }, 'Post extendable messages among service workers');
</script>
