/**
* 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;
}