Comparing version 1.7.9 to 1.8.0-beta.1
module.exports = ` | ||
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> | ||
<meta charset="UTF-8"><script type="text/javascript"> | ||
(function (win) { | ||
(function (self) { | ||
var init = function (e) { | ||
win.removeEventListener('message', init); | ||
self.removeEventListener('message', init); | ||
// eslint-disable-next-line no-eval | ||
(e && e.data && (typeof e.data.__init_uvm === 'string')) && eval(e.data.__init_uvm); | ||
}; | ||
win.addEventListener('message', init); | ||
}(window)); | ||
</script> | ||
</head></html>`; | ||
self.addEventListener('message', init); | ||
}(self)); | ||
`; |
/* | ||
* @note | ||
* options.bootTimeout is not implemented in browser sandbox. Reason is that as long as we use iFrame, there is no way | ||
* to interrupt an infinite loop. | ||
* options.bootTimeout is not implemented in browser sandbox because there is no way to interrupt an infinite loop. | ||
*/ | ||
var _ = require('lodash'), | ||
uuid = require('uuid'), | ||
var uuid = require('uuid'), | ||
Flatted = require('flatted'), | ||
MESSAGE = 'message', | ||
LOAD = 'load', | ||
ERROR = 'error', | ||
TARGET_ALL = '*', | ||
SANDBOX_ELEMENT_TAG = 'iframe', | ||
@@ -19,27 +14,14 @@ // code for bridge | ||
/** | ||
* Default DOM attributes of sandbox | ||
* @type {Object} | ||
*/ | ||
sandboxAttributes = { | ||
'class': 'postman-uvm-context', | ||
'style': 'display:none;', | ||
'sandbox': 'allow-scripts' | ||
}, | ||
/** | ||
* Returns the HTML to be executed inside iFrame. | ||
* Returns the firmware code to be executed inside Web Worker. | ||
* | ||
* @param {String} code | ||
* @param {String} id | ||
* @param {Boolean} firmwareOnly | ||
* @return {String} | ||
*/ | ||
sandboxCode = function (code, id, firmwareOnly) { | ||
var firmware = ` | ||
__uvm_emit = function (args) {window.parent.postMessage({__id_uvm: "${id}",__emit_uvm: args}, "*");}; | ||
sandboxFirmware = function (code, id) { | ||
return ` | ||
__uvm_emit = function (args) {self.postMessage({__id_uvm: "${id}",__emit_uvm: args});}; | ||
try {${code}} catch (e) { setTimeout(function () { throw e; }, 0); } | ||
(function (emit, id) { | ||
window.addEventListener("message", function (e) { | ||
self.addEventListener("message", function (e) { | ||
(e && e.data && (typeof e.data.__emit_uvm === 'string') && (e.data.__id_uvm === id)) && | ||
@@ -49,9 +31,4 @@ emit(e.data.__emit_uvm); | ||
}(__uvm_dispatch, "${id}")); | ||
__uvm_emit('${Flatted.stringify(['load.' + id])}'); | ||
__uvm_dispatch = null; __uvm_emit = null; | ||
`; | ||
return firmwareOnly ? firmware : `<!DOCTYPE html><html><head> | ||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><meta charset="UTF-8"> | ||
<script type="text/javascript">${firmware}</script></head></html>`; | ||
}; | ||
@@ -61,12 +38,7 @@ | ||
var id = uuid(), | ||
code = bridgeClientCode(options.bootCode), | ||
firmwareCode = sandboxFirmware(bridgeClientCode(options.bootCode), id), | ||
// make sure we escape the code only once | ||
serializedFirmware = unescape(encodeURIComponent(sandboxCode(code, id))), | ||
iframe = options._sandbox || document.createElement(SANDBOX_ELEMENT_TAG), | ||
// function to forward messages emitted | ||
forwardEmits = function (e) { | ||
if (!(e && e.data && _.isString(e.data.__emit_uvm) && (e.data.__id_uvm === id))) { return; } | ||
if (!(e && e.data && (typeof e.data.__emit_uvm === 'string') && (e.data.__id_uvm === id))) { return; } | ||
@@ -79,68 +51,67 @@ var args; | ||
processCallback = function () { | ||
!options._sandbox && iframe.removeEventListener(LOAD, processCallback); | ||
// function to forward errors emitted | ||
forwardErrors = function (e) { | ||
bridge.emit(ERROR, e); | ||
}, | ||
bridge._dispatch = function () { | ||
if (!iframe) { | ||
return bridge.emit(ERROR, | ||
new Error('uvm: unable to dispatch "' + arguments[0] + '" post disconnection.')); | ||
} | ||
firmwareObjectURL, | ||
worker; | ||
iframe.contentWindow.postMessage({ | ||
__emit_uvm: Flatted.stringify(Array.prototype.slice.call(arguments)), | ||
__id_uvm: id | ||
}, TARGET_ALL); | ||
}; | ||
// if sandbox worker is provided, we simply need to init with firmware code | ||
// @todo validate sandbox type or APIs | ||
if (options._sandbox) { | ||
worker = options._sandbox; | ||
worker.postMessage({ __init_uvm: firmwareCode }); | ||
} | ||
// else, spawn a new worker | ||
else { | ||
// convert the firmware code into a blob URL | ||
firmwareObjectURL = window.URL.createObjectURL( | ||
new Blob([firmwareCode], { type: 'text/javascript' }) | ||
); | ||
callback(null, bridge); | ||
}; | ||
worker = new Worker(firmwareObjectURL); | ||
} | ||
// add event listener for receiving events from iframe (is removed on disconnect) | ||
window.addEventListener(MESSAGE, forwardEmits); | ||
// add event listener for receiving events from worker (is removed on disconnect) | ||
// don't set `onmessage` and `onerror` as it might override external sandbox | ||
worker.addEventListener(MESSAGE, forwardEmits); | ||
worker.addEventListener(ERROR, forwardErrors); | ||
// equip bridge to disconnect (i.e. delete the iframe) | ||
bridge._disconnect = function () { | ||
if (!iframe) { return; } | ||
bridge._dispatch = function () { | ||
if (!worker) { | ||
return bridge.emit(ERROR, | ||
new Error('uvm: unable to dispatch "' + arguments[0] + '" post disconnection.')); | ||
} | ||
// remove the message handler for this sandbox | ||
window.removeEventListener(MESSAGE, forwardEmits); | ||
// do not delete sandbox element if not created for the bridge | ||
!options._sandbox && iframe.parentNode && iframe.parentNode.removeChild(iframe); | ||
iframe = null; | ||
worker.postMessage({ | ||
__emit_uvm: Flatted.stringify(Array.prototype.slice.call(arguments)), | ||
__id_uvm: id | ||
}); | ||
}; | ||
// if sandbox element is provided, we simply need to init with firmware code and wait for load event to be fired | ||
if (options._sandbox) { | ||
bridge.once('load.' + id, processCallback); // on load attach the dispatcher | ||
// equip bridge to disconnect (i.e. terminate the worker) | ||
bridge._disconnect = function () { | ||
if (!worker) { return; } | ||
iframe.contentWindow.postMessage({ | ||
__init_uvm: sandboxCode(code, id, true) // the last `true` param indicates firmwareOnly | ||
}, TARGET_ALL); | ||
// remove event listeners for this sandbox | ||
worker.removeEventListener(MESSAGE, forwardEmits); | ||
worker.removeEventListener(ERROR, forwardErrors); | ||
return; | ||
} | ||
// do not terminate sandbox worker if not spawned for the bridge | ||
if (!options._sandbox) { | ||
worker.terminate(); | ||
// this point onwards, it is our own iframe, so we do all the appending and other stuff to DOM | ||
iframe.addEventListener(LOAD, processCallback); // removed right when executed | ||
// revoke after termination. otherwise, blob reference is retained until GC | ||
// refer: "chrome://blob-internals" | ||
window.URL.revokeObjectURL(firmwareObjectURL); | ||
} | ||
// prepare an iframe as context | ||
_.forEach(sandboxAttributes, function (val, prop) { | ||
iframe.setAttribute(prop, val); | ||
}); | ||
worker = null; | ||
}; | ||
// add HTML and bootstrap code to the iframe | ||
iframe.setAttribute('src', 'data:text/html;base64, ' + btoa(serializedFirmware)); | ||
// help GC collect large variables | ||
firmwareCode = null; | ||
// data uri has size limits depending on the browsers | ||
// in browsers that don't support srcdoc attribute the src attribute is accepted | ||
// https://www.w3.org/TR/html5/semantics-embedded-content.html#an-iframe-srcdoc-document | ||
iframe.setAttribute('srcdoc', serializedFirmware); | ||
// now append the iframe to start processing stuff | ||
document.body.appendChild(iframe); | ||
// help GC collect large variables | ||
code = null; | ||
serializedFirmware = null; | ||
callback(null, bridge); | ||
}; |
{ | ||
"name": "uvm", | ||
"version": "1.7.9", | ||
"version": "1.8.0-beta.1", | ||
"description": "Universal Virtual Machine for Node and Browser", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
# uvm [![Build Status](https://travis-ci.org/postmanlabs/uvm.svg?branch=develop)](https://travis-ci.org/postmanlabs/uvm) [![codecov](https://codecov.io/gh/postmanlabs/uvm/branch/develop/graph/badge.svg)](https://codecov.io/gh/postmanlabs/uvm) | ||
Module that exposes an event emitter to send data across contexts (vm in node and iframe in browser) | ||
Module that exposes an event emitter to send data across contexts ([VM](https://nodejs.org/api/vm.html) in Node.js and [Web Workers](https://www.w3.org/TR/workers/) in browser) | ||
@@ -5,0 +5,0 @@ ## Usage |
@@ -1,19 +0,23 @@ | ||
(typeof window !== 'undefined' ? describe : describe.skip)('custom iframe in browser', function () { | ||
(typeof window !== 'undefined' ? describe : describe.skip)('custom sandbox in browser', function () { | ||
var uvm = require('../../lib'), | ||
firmware = require('../../firmware/sandbox-base'), | ||
iframe; | ||
firmwareUrl, | ||
worker; | ||
beforeEach(function (done) { | ||
iframe = document.createElement('iframe'); | ||
iframe.setAttribute('src', 'data:text/html;base64, ' + | ||
btoa(unescape(encodeURIComponent(firmware)))); | ||
iframe.addEventListener('load', function () { | ||
done(); | ||
}); | ||
document.body.appendChild(iframe); | ||
beforeEach(function () { | ||
firmwareUrl = window.URL.createObjectURL( | ||
new Blob([firmware], { type: 'text/javascript' }) | ||
); | ||
worker = new Worker(firmwareUrl); | ||
}); | ||
afterEach(function () { | ||
worker.terminate(); | ||
window.URL.revokeObjectURL(firmwareUrl); | ||
worker = firmwareUrl = null; | ||
}); | ||
it('should load and dispatch messages', function (done) { | ||
uvm.spawn({ | ||
_sandbox: iframe, | ||
_sandbox: worker, | ||
bootCode: ` | ||
@@ -35,7 +39,2 @@ bridge.on('loopback', function (data) { | ||
}); | ||
afterEach(function () { | ||
iframe.parentNode.removeChild(iframe); | ||
iframe = null; | ||
}); | ||
}); |
@@ -1,2 +0,2 @@ | ||
(typeof window !== 'undefined' ? describe : describe.skip)('custom iframe in browser', function () { | ||
(typeof window !== 'undefined' ? describe : describe.skip)('custom sandbox in browser', function () { | ||
var _ = require('lodash'), | ||
@@ -17,38 +17,36 @@ uvm = require('../../lib'), | ||
return ` | ||
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> | ||
<meta charset="UTF-8"> | ||
<script type="text/javascript"> | ||
${code} | ||
</script> | ||
<script type="text/javascript"> | ||
(function (win) { | ||
var init = function (e) { | ||
win.removeEventListener('message', init); | ||
// eslint-disable-next-line no-eval | ||
(e && e.data && (typeof e.data.__init_uvm === 'string')) && eval(e.data.__init_uvm); | ||
}; | ||
win.addEventListener('message', init); | ||
}(window)); | ||
</script> | ||
</head></html>`; | ||
${code} | ||
(function (self) { | ||
var init = function (e) { | ||
self.removeEventListener('message', init); | ||
// eslint-disable-next-line no-eval | ||
(e && e.data && (typeof e.data.__init_uvm === 'string')) && eval(e.data.__init_uvm); | ||
}; | ||
self.addEventListener('message', init); | ||
}(self)); | ||
`; | ||
}, | ||
iframe; | ||
firmwareUrl, | ||
worker; | ||
beforeEach(function (done) { | ||
var fakeBundleSize = 5 * 1024 * 1024, // 10mb (5 million characters with 2 bytes each) | ||
beforeEach(function () { | ||
var fakeBundleSize = 5 * 1024 * 1024, // 10MB (5 million characters with 2 bytes each) | ||
largeJSStatement = `var x = '${getLargeString(fakeBundleSize)}';`; | ||
iframe = document.createElement('iframe'); | ||
iframe.setAttribute('src', 'data:text/html;base64, ' + | ||
btoa(unescape(encodeURIComponent(getFirmware(largeJSStatement))))); | ||
iframe.setAttribute('srcdoc', unescape(encodeURIComponent(getFirmware(largeJSStatement)))); | ||
iframe.addEventListener('load', function () { | ||
done(); | ||
}); | ||
document.body.appendChild(iframe); | ||
firmwareUrl = window.URL.createObjectURL( | ||
new Blob([getFirmware(largeJSStatement)], { type: 'text/javascript' }) | ||
); | ||
worker = new Worker(firmwareUrl); | ||
}); | ||
afterEach(function () { | ||
worker.terminate(); | ||
window.URL.revokeObjectURL(firmwareUrl); | ||
worker = firmwareUrl = null; | ||
}); | ||
it('should load and dispatch messages', function (done) { | ||
uvm.spawn({ | ||
_sandbox: iframe, | ||
_sandbox: worker, | ||
bootCode: ` | ||
@@ -70,7 +68,2 @@ bridge.on('loopback', function (data) { | ||
}); | ||
afterEach(function () { | ||
iframe.parentNode.removeChild(iframe); | ||
iframe = null; | ||
}); | ||
}); |
((typeof window === 'undefined') ? describe : describe.skip)('uvm timeout option', function () { | ||
var uvm = require('../../lib'); | ||
// options.bootTimeout is not implemented in browser sandbox. Reason is that as long as we use iFrame, there is no | ||
// way to interrupt an infinite loop. | ||
// options.bootTimeout is not implemented in browser sandbox because there | ||
// is no way to interrupt an infinite loop. | ||
it('must exit if bootCode has infinite loop', function (done) { | ||
@@ -23,4 +23,4 @@ uvm.spawn({ | ||
// options.dispatchTimeout is not implemented in browser sandbox. Reason is that as long as we use iFrame, there is | ||
// no way to interrupt an infinite loop. | ||
// options.dispatchTimeout is not implemented in browser sandbox because | ||
// there is no way to interrupt an infinite loop. | ||
it('must exit if dispatch is has infinite loop', function (done) { | ||
@@ -27,0 +27,0 @@ uvm.spawn({ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
123147
2010
1