dcp-client
Version:
Core libraries for accessing DCP network
117 lines (107 loc) • 4.37 kB
JavaScript
/**
* @file evaluator-lib/script-load-wrapper.js
*
* This file provides a global function for all proceeding scripts to wrap their
* initialization. It will post messages about the success/failure of the script
* and handles wrapping of post message when the flag is set.
*
* @author Ryan Rossiter, ryan@kingsds.network
* @date September 2020
*/
(function scriptLoadWrapper()
{
/* protectedStorage - passed as 1st arg to all script wrapScriptLoading fns. Private memo area where
* various scripts can leave each things they will need but cannot share in global scope. Must not
* leak any of these properties to work functions.
*/
const protectedStorage = {};
/* Add a console symbol to protectedStorage which is either the "real" console or a group of noops */
protectedStorage.console = console || { };
for (let method of ['log', 'error', 'debug', 'trace', 'warn'])
if (typeof protectedStorage.console[method] !== 'function')
protectedStorage.console[method] = function(){};
/**
* Wrap self.postMessage so we have a ringSource property. Allows checking for
* the validity of a message in the sandbox. Setting currPostMessage - self.postMessage
* as a constant, then always re-defining self.postMessage in terms of the initial
* currPostMessage ensures we don't recursively add layers to our wrapping of postMessage.
*/
let currentRing = -1;
const currPostMessage = self.postMessage;
const marshal = KVIN.marshal
const serialize = JSON.stringify
function wrapPostMessage() {
const ringSource = ++currentRing;
self.postMessage = function (value) {
// console.log(value);
// Objects may not be transferable objects (https://developer.mozilla.org/en-US/docs/Glossary/Transferable_objects),
// and can remain non-transferable even after kvin.marshal, and it is very hard to detect such objects. One such object
// is the `arguments` object of any function. In such a case, we need to serialize the message on top of
const updatedMsg = marshal({ ringSource, value });
try {
currPostMessage(updatedMsg);
}
catch (e) {
const serializedMessage = {
message: serialize(updatedMsg),
serialized: true,
};
currPostMessage(serializedMessage);
}
}
}
//Initialize postMessage to ring 0
wrapPostMessage()
const ring0PostMessage = self.postMessage;
/**
* This function is used by evaluator scripts to wrap their evaluation so that
* errors can be caught and reported, and to discourage pollution of the global
* scope by enclosing them in a function scope.
*
* @param {object} options
* @param {string} options.scriptName The name of the script that is being loaded
* @param {boolean} [options.ringTransition] When true, the global postMessage ring will be incremented before the function is invoked
* @param {boolean} [options.finalScript] When true, the wrapScriptLoading function will be removed from the global scope afterwards
* @param {function} fn
*/
self.wrapScriptLoading = function scriptLoadWrapper$wrapScriptLoading(options, fn) {
try {
// capture the current postMessage before transitioning rings
const fixedPostMessage = self.postMessage;
if (options.ringTransition) {
wrapPostMessage();
}
fn(protectedStorage, fixedPostMessage, wrapPostMessage)
ring0PostMessage({
request: 'scriptLoaded',
script: options.scriptName,
result: "success",
});
if (options.finalScript) {
delete self.wrapScriptLoading;
// The private '__sandboxLoaded' event is used by dcp-native; do not remove.
// Otherwise, do not use __sandboxLoaded.
ring0PostMessage({
request: '__sandboxLoaded', // SAVE
});
}
} catch (e) {
ring0PostMessage({
request: 'scriptLoaded',
script: options.scriptName,
result: "failure",
error: {
name: e.name,
message: e.message,
stack: e.stack.replace(
/data:application\/javascript.*?:/g,
'eval:'
),
}
});
}
}
})();
self.wrapScriptLoading({ scriptName: 'script-load-wrapper' }, () => {
// noop
});