/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint-env amd */
"use strict";
/* global workerHelper */
/* exported workerHelper */
(
function (root, factory) {
if (
typeof define ===
"function" && define.amd) {
define(factory);
}
else if (
typeof exports ===
"object") {
module.exports = factory();
}
else {
root.workerHelper = factory();
}
})(
this,
function () {
/**
* This file is to only be included by ChromeWorkers. This exposes
* a `createTask` function to workers to register tasks for communication
* back to `devtools/shared/worker`.
*
* Tasks can be send their responses via a return value, either a primitive
* or a promise.
*
* createTask(self, "average", function (data) {
* return data.reduce((sum, val) => sum + val, 0) / data.length;
* });
*
* createTask(self, "average", function (data) {
* return new Promise((resolve, reject) => {
* resolve(data.reduce((sum, val) => sum + val, 0) / data.length);
* });
* });
*
*
* Errors:
*
* Returning an Error value, or if the returned promise is rejected, this
* propagates to the DevToolsWorker as a rejected promise. If an error is
* thrown in a synchronous function, that error is also propagated.
*/
/**
* Takes a worker's `self` object, a task name, and a function to
* be called when that task is called. The task is called with the
* passed in data as the first argument
*
* @param {object} self
* @param {string} name
* @param {function} fn
*/
function createTask(self, name, fn) {
// Store a hash of task name to function on the Worker
if (!self._tasks) {
self._tasks = {};
}
// Create the onmessage handler if not yet created.
if (!self.onmessage) {
self.onmessage = createHandler(self);
}
// Store the task on the worker.
self._tasks[name] = fn;
}
/**
* Creates the `self.onmessage` handler for a Worker.
*
* @param {object} self
* @return {function}
*/
function createHandler(self) {
return function (e) {
const { id, task, data } = e.data;
const taskFn = self._tasks[task];
if (!taskFn) {
self.postMessage({ id, error: `Task
"${task}" not found in worker.` });
return;
}
try {
handleResponse(taskFn(data));
}
catch (ex) {
handleError(ex);
}
function handleResponse(response) {
// If a promise
if (response &&
typeof response.then ===
"function") {
response.then(
val => self.postMessage({ id, response: val }),
handleError
);
}
else if (response
instanceof Error) {
// If an error object
handleError(response);
}
else {
// If anything else
self.postMessage({ id, response });
}
}
function handleError(error =
"Error") {
try {
// First, try and structured clone the error across directly.
self.postMessage({ id, error });
}
catch (x) {
// We could not clone whatever error value was given. Do our best to
// stringify it.
let errorString = `Error
while performing task
"${task}": `;
try {
errorString += error.toString();
}
catch (ex) {
errorString +=
"";
}
if (
"stack" in error) {
try {
errorString +=
"\n" + error.stack;
}
catch (err) {
// Do nothing
}
}
self.postMessage({ id, error: errorString });
}
}
};
}
return { createTask };
});