Source: workers.js

/**
 * This module provides functionality for Web Workers to listen for 
 * background script events generated by WebScience. The design pattern
 * is simple: for each event you want your worker script to observe, use
 * `createEventListener` to generate a listener, then add the listener
 * to the event. The function will automatically convert background
 * script events to messages posted to your worker script.
 * 
 * ## Design Considerations
 * We use message passing for WebScience integration with Web Workers,
 * because of Content Security Policy considerations. Specifically, if
 * we were to replicate WebScience APIs inside of worker scripts, we
 * would need to load inlined worker scripts from the background page.
 * There is, however, no way to permit only specific inline worker
 * scripts in Content Security Policy Level 3. We would have to allow
 * all blob URLs to load as worker scripts, which may not be permissible
 * for addons.mozilla.org review.
 * 
 * ## Future Directions
 * If we identify a path forward for loading worker scripts from blob
 * URLs, we could enable a more streamlined design pattern for WebScience
 * event listeners in worker scripts.
 * 
 * @module workers
 */

/**
 * An object that is posted as a message to a Web Worker when an
 * event fires. 
 * @typedef {Object} WorkerEventData
 * @property {string} eventName - The name of the event.
 * @property {Array} listenerArguments - The arguments that would be
 * passed to an event listener in the background script.
 * @example
 * // {
 * //   eventName: "webScience.scheduling.onIdleDaily",
 * //   listenerArguments: [ ]
 * // }
 * @example
 * // {
 * //   eventName: "webScience.idle.onStateChanged",
 * //   listenerArguments: [ "idle" ]
 * // }
 * @example
  * // {
 * //   eventName: "webScience.pageNavigation.onPageData",
 * //   listenerArguments: [{
 * //     pageId: "e60f1f92-f42b-4084-93a7-9e7145e5f716",
 * //     url: "https://www.example.com/",
 * //     ...
 * //   }]
 * // }
 */

/**
 * Create a listener for a WebScience event that will automatically
 * send a message to a Worker when the event occurs.
 * @param {Worker} worker - The Worker that should receive messages
 * about the event.
 * @param {string} [eventName] - The name of the event, which is used
 * as the `eventName` property in messages to the Worker. This parameter
 * is not necessary when adding the listener to a WebScience event or
 * an event created with `events.createEvent` (if an an event name is
 * specified), because `eventName` (if not provided as a parameter) will
 * be automatically set to the event's name when the listener is added to
 * the event.
 * @returns {Function} A listener for the event. Each listener should
 * only be used with one event.
 * @example
 * // An example of how to use `workers.createEventListener` for
 * // natural language processing in a Web Worker.
 * 
 * // background.js
 * const worker = new Worker("worker.js");
 * webScience.pageText.onTextParsed.addListener(
 *   webScience.workers.createEventListener(worker),
 *   {
 *     matchPatterns: [ "*://*.example.com/*" ]
 *   });
 * 
 * // worker.js
 * function onTextParsedListener(textParsedDetails) {
 *   // Natural language processing on webpage text
 * }
 * self.addEventListener("message", event => {
 *   if((typeof event.data === "object") &&
 *      ("eventName" in event.data) &&
 *      (event.data.eventName === "webScience.pageText.onTextParsed")) {
 *     onTextParsedListener.apply(null, event.data.listenerArguments);
 *   }
 * });
 */
export function createEventListener(worker, eventName = null) {
    const listener = function(...args) {
        // If there was an event name parameter, use that as the event name for the message to the worker
        // If there was no event name parameter but an event name annotation was set on the listener
        // function, use that as the event name for the message to the worker
        // Only send a message to the worker if we have an event name for the message
        let messageEventName = null;
        if(typeof eventName === "string") {
            messageEventName = eventName;
        }
        else if(("webScienceEventName" in listener) && (typeof listener.webScienceEventName === "string")) {
            messageEventName = listener.webScienceEventName;
        }
        if(typeof messageEventName === "string") {
            worker.postMessage({
                eventName: messageEventName,
                listenerArguments: args
            });
        }
    };
    return listener;
}