'use strict';

// This script depends on the following scripts:
//    /fs/resources/messaging-helpers.js
//    /fs/resources/test-helpers.js

// add_message_event_handlers() is the helper function used to setup all
// message targets, including iframes and workers.
//
// Adds a message event handler and a message error handler to |receiver|.
// The 'data' property from received MessageEvents must include a 'type'
// property. The 'type' selects the test logic to run. Most message type
// handlers use postMessage() to respond to the sender with test results.
// The sender then validates the test results after receiving the response.
//
// Both |target| and |target_origin| are optional. |target| is used
// to send message responses back to the sender. When omitted, the
// 'source' from received MessageEvents is used instead.
//
// For window messaging, |target_origin| specifies the origin to receive
// responses. Most window tests use '*' for the |target_origin|. Worker
// and message port tests must use undefined for |target_origin| to avoid
// exceptions.
function add_message_event_handlers(receiver, target, target_origin) {
  receiver.addEventListener('message', async function (message_event) {
    const message_data = message_event.data;

    // Reply to the sender using the 'source' from the received MessageEvent.
    let message_source = message_event.source;
    if (message_source === null) {
      // However, some message senders, like DedicatedWorkers, don't include
      // a source. Fallback to the target when the source is null.
      message_source = target;
    }

    try {
      switch (message_data.type) {
        case 'receive-message-port':
          // Receive a MessagePort to use as a message target for testing.
          add_message_event_handlers(
            /*receiver=*/message_data.message_port,
            /*target=*/message_data.message_port);
          message_data.message_port.start();
          break;

        case 'create-broadcast-channel':
          // Create a BroadcastChannel to use as a message target for testing.
          const broadcast_channel =
            new BroadcastChannel(message_data.broadcast_channel_name);
          add_message_event_handlers(
            /*receiver=*/broadcast_channel,
            /*target=*/broadcast_channel);
          message_source.postMessage(
            { type: 'broadcast-channel-created' },
            { targetOrigin: target_origin });
          break;

        case 'receive-file-system-handles':
          // Receive a list of cloned FileSystemFileHandles. Access the
          // properties of each FileSystemFileHandle by serializing the
          // handle to a JavaScript object. Then respond with the serialized
          // results, enabling the sender to verify that the cloned handle
          // produced the expected property values from this execution context.
          const serialized_handles = [];
          const cloned_handles = message_data.cloned_handles;
          for (let i = 0; i < cloned_handles.length; ++i) {
            const serialized = await serialize_handle(cloned_handles[i]);
            serialized_handles.push(serialized);
          }
          message_source.postMessage({
            type: 'receive-serialized-file-system-handles',
            serialized_handles,
            // Respond with the cloned handles to create new clones for
            // the sender to verify.
            cloned_handles,
          }, { targetOrigin: target_origin });
          break;

        case 'receive-serialized-file-system-handles':
          // Do nothing. This message is meant for test runner validation.
          // Other message targets may receive this message while testing
          // broadcast channels.
          break;

        case 'create-file':
          // Create a new file and then respond to the sender with it.
          const directory = await navigator.storage.getDirectory();
          const file_handle =
            await directory.getFileHandle('temp-file', { create: true });
          message_source.postMessage(
            { type: 'receive-file', file_handle },
            { targetOrigin: target_origin });
          break;

        case 'create-directory':
          // Create a new directory and then respond to the sender with it.
          const parent_directory = await navigator.storage.getDirectory();
          const directory_handle =
            await parent_directory.getDirectoryHandle('temp-directory',
              { create: true });
          message_source.postMessage(
            { type: 'receive-directory', directory_handle },
            { targetOrigin: target_origin });
          break;

        case 'create-sync-access-handle':
          // Receive a file and create a sync access handle out of it. Report
          // success to the sender.
          let success = true;
          try {
            const access_handle =
                await message_data.file_handle.createSyncAccessHandle();
            access_handle.close();
          } catch (error) {
            success = false;
          }

          message_source.postMessage(
            { type: 'receive-sync-access-handle-result', success },
            { targetOrigin: target_origin });
          break;

        default:
          throw `Unknown message type: '${message_data.type}'`;
      }
    } catch (error) {
      // Respond with an error to trigger a failure in the sender's
      // test runner.
      message_source.postMessage(`ERROR: ${error}`,
        { targetOrigin: target_origin });
    }
  });

  receiver.addEventListener('messageerror', async function (message_event) {
    // Select the target for message responses (see comment in 'message' event
    // listener above).
    let message_source = message_event.source;
    if (message_source === null) {
      message_source = target;
    }

    try {
      // Respond with the MessageEvent's property values, enabling the sender
      // to verify results.
      const serialized_message_error_event =
        serialize_message_error_event(message_event);
      message_source.postMessage({
        type: 'serialized-message-error',
        serialized_message_error_event
      }, { targetOrigin: target_origin });
    } catch (error) {
      // Respond with an error to trigger a failure in the sender's
      // test runner.
      message_source.postMessage(`ERROR: ${error}`,
        { targetOrigin: target_origin });
    }
  });
}
