Merge pull request #7878 from baguzzzaji/remove-jailed-and-all-references-to-it
Remove jailed and all references to it
This commit is contained in:
@ -1,432 +0,0 @@
|
|||||||
|
|
||||||
/**
|
|
||||||
* Contains the JailedSite object used both by the application
|
|
||||||
* site, and by each plugin
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function(){
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JailedSite object represents a single site in the
|
|
||||||
* communication protocol between the application and the plugin
|
|
||||||
*
|
|
||||||
* @param {Object} connection a special object allowing to send
|
|
||||||
* and receive messages from the opposite site (basically it
|
|
||||||
* should only provide send() and onMessage() methods)
|
|
||||||
*/
|
|
||||||
JailedSite = function(connection) {
|
|
||||||
this._interface = {};
|
|
||||||
this._remote = null;
|
|
||||||
this._remoteUpdateHandler = function(){};
|
|
||||||
this._getInterfaceHandler = function(){};
|
|
||||||
this._interfaceSetAsRemoteHandler = function(){};
|
|
||||||
this._disconnectHandler = function(){};
|
|
||||||
this._store = new ReferenceStore;
|
|
||||||
|
|
||||||
var me = this;
|
|
||||||
this._connection = connection;
|
|
||||||
this._connection.onMessage(
|
|
||||||
function(data){ me._processMessage(data); }
|
|
||||||
);
|
|
||||||
|
|
||||||
this._connection.onDisconnect(
|
|
||||||
function(m){
|
|
||||||
me._disconnectHandler(m);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a handler to be called when the remote site updates its
|
|
||||||
* interface
|
|
||||||
*
|
|
||||||
* @param {Function} handler
|
|
||||||
*/
|
|
||||||
JailedSite.prototype.onRemoteUpdate = function(handler) {
|
|
||||||
this._remoteUpdateHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a handler to be called when received a responce from the
|
|
||||||
* remote site reporting that the previously provided interface
|
|
||||||
* has been succesfully set as remote for that site
|
|
||||||
*
|
|
||||||
* @param {Function} handler
|
|
||||||
*/
|
|
||||||
JailedSite.prototype.onInterfaceSetAsRemote = function(handler) {
|
|
||||||
this._interfaceSetAsRemoteHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a handler to be called when the remote site requests to
|
|
||||||
* (re)send the interface. Used to detect an initialzation
|
|
||||||
* completion without sending additional request, since in fact
|
|
||||||
* 'getInterface' request is only sent by application at the last
|
|
||||||
* step of the plugin initialization
|
|
||||||
*
|
|
||||||
* @param {Function} handler
|
|
||||||
*/
|
|
||||||
JailedSite.prototype.onGetInterface = function(handler) {
|
|
||||||
this._getInterfaceHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Object} set of remote interface methods
|
|
||||||
*/
|
|
||||||
JailedSite.prototype.getRemote = function() {
|
|
||||||
return this._remote;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the interface of this site making it available to the
|
|
||||||
* remote site by sending a message with a set of methods names
|
|
||||||
*
|
|
||||||
* @param {Object} _interface to set
|
|
||||||
*/
|
|
||||||
JailedSite.prototype.setInterface = function(_interface) {
|
|
||||||
this._interface = _interface;
|
|
||||||
this._sendInterface();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the actual interface to the remote site upon it was
|
|
||||||
* updated or by a special request of the remote site
|
|
||||||
*/
|
|
||||||
JailedSite.prototype._sendInterface = function() {
|
|
||||||
var names = [];
|
|
||||||
for (var name in this._interface) {
|
|
||||||
if (this._interface.hasOwnProperty(name)) {
|
|
||||||
names.push(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._connection.send({type:'setInterface', api: names});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a message from the remote site
|
|
||||||
*/
|
|
||||||
JailedSite.prototype._processMessage = function(data) {
|
|
||||||
switch(data.type) {
|
|
||||||
case 'method':
|
|
||||||
var method = this._interface[data.name];
|
|
||||||
var args = this._unwrap(data.args);
|
|
||||||
method.apply(null, args);
|
|
||||||
break;
|
|
||||||
case 'callback':
|
|
||||||
var method = this._store.fetch(data.id)[data.num];
|
|
||||||
var args = this._unwrap(data.args);
|
|
||||||
method.apply(null, args);
|
|
||||||
break;
|
|
||||||
case 'setInterface':
|
|
||||||
this._setRemote(data.api);
|
|
||||||
break;
|
|
||||||
case 'getInterface':
|
|
||||||
this._sendInterface();
|
|
||||||
this._getInterfaceHandler();
|
|
||||||
break;
|
|
||||||
case 'interfaceSetAsRemote':
|
|
||||||
this._interfaceSetAsRemoteHandler();
|
|
||||||
break;
|
|
||||||
case 'disconnect':
|
|
||||||
this._disconnectHandler();
|
|
||||||
this._connection.disconnect();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a requests to the remote site asking it to provide its
|
|
||||||
* current interface
|
|
||||||
*/
|
|
||||||
JailedSite.prototype.requestRemote = function() {
|
|
||||||
this._connection.send({type:'getInterface'});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the new remote interface provided by the other site
|
|
||||||
*
|
|
||||||
* @param {Array} names list of function names
|
|
||||||
*/
|
|
||||||
JailedSite.prototype._setRemote = function(names) {
|
|
||||||
this._remote = {};
|
|
||||||
var i, name;
|
|
||||||
for (i = 0; i < names.length; i++) {
|
|
||||||
name = names[i];
|
|
||||||
this._remote[name] = this._genRemoteMethod(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._remoteUpdateHandler();
|
|
||||||
this._reportRemoteSet();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the wrapped function corresponding to a single remote
|
|
||||||
* method. When the generated function is called, it will send the
|
|
||||||
* corresponding message to the remote site asking it to execute
|
|
||||||
* the particular method of its interface
|
|
||||||
*
|
|
||||||
* @param {String} name of the remote method
|
|
||||||
*
|
|
||||||
* @returns {Function} wrapped remote method
|
|
||||||
*/
|
|
||||||
JailedSite.prototype._genRemoteMethod = function(name) {
|
|
||||||
var me = this;
|
|
||||||
var remoteMethod = function() {
|
|
||||||
me._connection.send({
|
|
||||||
type: 'method',
|
|
||||||
name: name,
|
|
||||||
args: me._wrap(arguments)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return remoteMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a responce reporting that interface just provided by the
|
|
||||||
* remote site was sucessfully set by this site as remote
|
|
||||||
*/
|
|
||||||
JailedSite.prototype._reportRemoteSet = function() {
|
|
||||||
this._connection.send({type:'interfaceSetAsRemote'});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares the provided set of remote method arguments for
|
|
||||||
* sending to the remote site, replaces all the callbacks with
|
|
||||||
* identifiers
|
|
||||||
*
|
|
||||||
* @param {Array} args to wrap
|
|
||||||
*
|
|
||||||
* @returns {Array} wrapped arguments
|
|
||||||
*/
|
|
||||||
JailedSite.prototype._wrap = function(args) {
|
|
||||||
var wrapped = [];
|
|
||||||
var callbacks = {};
|
|
||||||
var callbacksPresent = false;
|
|
||||||
for (var i = 0; i < args.length; i++) {
|
|
||||||
if (typeof args[i] == 'function') {
|
|
||||||
callbacks[i] = args[i];
|
|
||||||
wrapped[i] = {type: 'callback', num : i};
|
|
||||||
callbacksPresent = true;
|
|
||||||
} else {
|
|
||||||
wrapped[i] = {type: 'argument', value : args[i]};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = {args: wrapped};
|
|
||||||
|
|
||||||
if (callbacksPresent) {
|
|
||||||
result.callbackId = this._store.put(callbacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unwraps the set of arguments delivered from the remote site,
|
|
||||||
* replaces all callback identifiers with a function which will
|
|
||||||
* initiate sending that callback identifier back to other site
|
|
||||||
*
|
|
||||||
* @param {Object} args to unwrap
|
|
||||||
*
|
|
||||||
* @returns {Array} unwrapped args
|
|
||||||
*/
|
|
||||||
JailedSite.prototype._unwrap = function(args) {
|
|
||||||
var called = false;
|
|
||||||
|
|
||||||
// wraps each callback so that the only one could be called
|
|
||||||
var once = function(cb) {
|
|
||||||
return function() {
|
|
||||||
if (!called) {
|
|
||||||
called = true;
|
|
||||||
cb.apply(this, arguments);
|
|
||||||
} else {
|
|
||||||
var msg =
|
|
||||||
'A callback from this set has already been executed';
|
|
||||||
throw new Error(msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = [];
|
|
||||||
var i, arg, cb, me = this;
|
|
||||||
for (i = 0; i < args.args.length; i++) {
|
|
||||||
arg = args.args[i];
|
|
||||||
if (arg.type == 'argument') {
|
|
||||||
result.push(arg.value);
|
|
||||||
} else {
|
|
||||||
cb = once(
|
|
||||||
this._genRemoteCallback(args.callbackId, i)
|
|
||||||
);
|
|
||||||
result.push(cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the wrapped function corresponding to a single remote
|
|
||||||
* callback. When the generated function is called, it will send
|
|
||||||
* the corresponding message to the remote site asking it to
|
|
||||||
* execute the particular callback previously saved during a call
|
|
||||||
* by the remote site a method from the interface of this site
|
|
||||||
*
|
|
||||||
* @param {Number} id of the remote callback to execute
|
|
||||||
* @param {Number} argNum argument index of the callback
|
|
||||||
*
|
|
||||||
* @returns {Function} wrapped remote callback
|
|
||||||
*/
|
|
||||||
JailedSite.prototype._genRemoteCallback = function(id, argNum) {
|
|
||||||
var me = this;
|
|
||||||
var remoteCallback = function() {
|
|
||||||
me._connection.send({
|
|
||||||
type : 'callback',
|
|
||||||
id : id,
|
|
||||||
num : argNum,
|
|
||||||
args : me._wrap(arguments)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return remoteCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the notification message and breaks the connection
|
|
||||||
*/
|
|
||||||
JailedSite.prototype.disconnect = function() {
|
|
||||||
this._connection.send({type: 'disconnect'});
|
|
||||||
this._connection.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a handler to be called when received a disconnect message
|
|
||||||
* from the remote site
|
|
||||||
*
|
|
||||||
* @param {Function} handler
|
|
||||||
*/
|
|
||||||
JailedSite.prototype.onDisconnect = function(handler) {
|
|
||||||
this._disconnectHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ReferenceStore is a special object which stores other objects
|
|
||||||
* and provides the references (number) instead. This reference
|
|
||||||
* may then be sent over a json-based communication channel (IPC
|
|
||||||
* to another Node.js process or a message to the Worker). Other
|
|
||||||
* site may then provide the reference in the responce message
|
|
||||||
* implying the given object should be activated.
|
|
||||||
*
|
|
||||||
* Primary usage for the ReferenceStore is a storage for the
|
|
||||||
* callbacks, which therefore makes it possible to initiate a
|
|
||||||
* callback execution by the opposite site (which normally cannot
|
|
||||||
* directly execute functions over the communication channel).
|
|
||||||
*
|
|
||||||
* Each stored object can only be fetched once and is not
|
|
||||||
* available for the second time. Each stored object must be
|
|
||||||
* fetched, since otherwise it will remain stored forever and
|
|
||||||
* consume memory.
|
|
||||||
*
|
|
||||||
* Stored object indeces are simply the numbers, which are however
|
|
||||||
* released along with the objects, and are later reused again (in
|
|
||||||
* order to postpone the overflow, which should not likely happen,
|
|
||||||
* but anyway).
|
|
||||||
*/
|
|
||||||
var ReferenceStore = function() {
|
|
||||||
this._store = {}; // stored object
|
|
||||||
this._indices = [0]; // smallest available indices
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @function _genId() generates the new reference id
|
|
||||||
*
|
|
||||||
* @returns {Number} smallest available id and reserves it
|
|
||||||
*/
|
|
||||||
ReferenceStore.prototype._genId = function() {
|
|
||||||
var id;
|
|
||||||
if (this._indices.length == 1) {
|
|
||||||
id = this._indices[0]++;
|
|
||||||
} else {
|
|
||||||
id = this._indices.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases the given reference id so that it will be available by
|
|
||||||
* another object stored
|
|
||||||
*
|
|
||||||
* @param {Number} id to release
|
|
||||||
*/
|
|
||||||
ReferenceStore.prototype._releaseId = function(id) {
|
|
||||||
for (var i = 0; i < this._indices.length; i++) {
|
|
||||||
if (id < this._indices[i]) {
|
|
||||||
this._indices.splice(i, 0, id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleaning-up the sequence tail
|
|
||||||
for (i = this._indices.length-1; i >= 0; i--) {
|
|
||||||
if (this._indices[i]-1 == this._indices[i-1]) {
|
|
||||||
this._indices.pop();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the given object and returns the refernce id instead
|
|
||||||
*
|
|
||||||
* @param {Object} obj to store
|
|
||||||
*
|
|
||||||
* @returns {Number} reference id of the stored object
|
|
||||||
*/
|
|
||||||
ReferenceStore.prototype.put = function(obj) {
|
|
||||||
var id = this._genId();
|
|
||||||
this._store[id] = obj;
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves previously stored object and releases its reference
|
|
||||||
*
|
|
||||||
* @param {Number} id of an object to retrieve
|
|
||||||
*/
|
|
||||||
ReferenceStore.prototype.fetch = function(id) {
|
|
||||||
var obj = this._store[id];
|
|
||||||
this._store[id] = null;
|
|
||||||
delete this._store[id];
|
|
||||||
this._releaseId(id);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
<script sandbox="allow-same-origin allow-scripts" src="_frame.js"></script>
|
|
@ -1,56 +0,0 @@
|
|||||||
|
|
||||||
/**
|
|
||||||
* Contains the code executed in the sandboxed frame under web-browser
|
|
||||||
*
|
|
||||||
* Creates a Web-Worker inside the frame, sets up the communication
|
|
||||||
* between the worker and the parent window
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
var scripts = document.getElementsByTagName('script');
|
|
||||||
var __jailed__path__ = scripts[scripts.length-1].src
|
|
||||||
.split('?')[0]
|
|
||||||
.split('/')
|
|
||||||
.slice(0, -1)
|
|
||||||
.join('/') + '/';
|
|
||||||
|
|
||||||
// creating worker as a blob enables import of local files
|
|
||||||
var blobCode = [
|
|
||||||
' self.addEventListener("message", function(m){ ',
|
|
||||||
' if (m.data.type == "initImport") { ',
|
|
||||||
' importScripts(m.data.url); ',
|
|
||||||
' self.postMessage({type: "initialized"}); ',
|
|
||||||
' } ',
|
|
||||||
' }); '
|
|
||||||
].join('\n');
|
|
||||||
|
|
||||||
var blobUrl;
|
|
||||||
try {
|
|
||||||
blobUrl = new Blob([blobCode], {type: 'application/javascript'});
|
|
||||||
} catch (e) {
|
|
||||||
window.BlobBuilder = window.BlobBuilder
|
|
||||||
|| window.WebKitBlobBuilder
|
|
||||||
|| window.MozBlobBuilder;
|
|
||||||
blobUrl = new BlobBuilder();
|
|
||||||
blobUrl.append(blobCode);
|
|
||||||
blobUrl = blobUrl.getBlob();
|
|
||||||
}
|
|
||||||
|
|
||||||
var worker = new Worker(URL.createObjectURL(blobUrl));
|
|
||||||
|
|
||||||
// telling worker to load _pluginWeb.js (see blob code above)
|
|
||||||
worker.postMessage({
|
|
||||||
type: 'initImport',
|
|
||||||
url: __jailed__path__ + '_pluginWeb.js'
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// forwarding messages between the worker and parent window
|
|
||||||
worker.addEventListener('message', function(m) {
|
|
||||||
parent.postMessage(m.data, '*');
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('message', function(m) {
|
|
||||||
worker.postMessage(m.data);
|
|
||||||
});
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
|||||||
|
|
||||||
/**
|
|
||||||
* Core plugin script loaded into the plugin process/thread.
|
|
||||||
*
|
|
||||||
* Initializes the plugin-site API global methods.
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function(){
|
|
||||||
|
|
||||||
// localize
|
|
||||||
var site = new JailedSite(connection);
|
|
||||||
delete JailedSite;
|
|
||||||
delete connection;
|
|
||||||
|
|
||||||
site.onGetInterface(function(){
|
|
||||||
launchConnected();
|
|
||||||
});
|
|
||||||
|
|
||||||
site.onRemoteUpdate(function(){
|
|
||||||
application.remote = site.getRemote();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simplified clone of Whenable instance (the object can not be
|
|
||||||
* placed into a shared script, because the main library needs it
|
|
||||||
* before the additional scripts may load)
|
|
||||||
*/
|
|
||||||
var connected = false;
|
|
||||||
var connectedHandlers = [];
|
|
||||||
|
|
||||||
var launchConnected = function() {
|
|
||||||
if (!connected) {
|
|
||||||
connected = true;
|
|
||||||
|
|
||||||
var handler;
|
|
||||||
while(handler = connectedHandlers.pop()) {
|
|
||||||
handler();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var checkHandler = function(handler){
|
|
||||||
var type = typeof handler;
|
|
||||||
if (type != 'function') {
|
|
||||||
var msg =
|
|
||||||
'A function may only be subsribed to the event, '
|
|
||||||
+ type
|
|
||||||
+ ' was provided instead'
|
|
||||||
throw new Error(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a function executed after the connection to the
|
|
||||||
* application is estaplished, and the initial interface-exchange
|
|
||||||
* messaging is completed
|
|
||||||
*
|
|
||||||
* @param {Function} handler to be called upon initialization
|
|
||||||
*/
|
|
||||||
application.whenConnected = function(handler) {
|
|
||||||
handler = checkHandler(handler);
|
|
||||||
if (connected) {
|
|
||||||
handler();
|
|
||||||
} else {
|
|
||||||
connectedHandlers.push(handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the plugin interface available to the application
|
|
||||||
*
|
|
||||||
* @param {Object} _interface to set
|
|
||||||
*/
|
|
||||||
application.setInterface = function(_interface) {
|
|
||||||
site.setInterface(_interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnects the plugin from the application (sending
|
|
||||||
* notification message) and destroys itself
|
|
||||||
*/
|
|
||||||
application.disconnect = function(_interface) {
|
|
||||||
site.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
@ -1,267 +0,0 @@
|
|||||||
|
|
||||||
/**
|
|
||||||
* Contains the routines loaded by the plugin process under Node.js
|
|
||||||
*
|
|
||||||
* Initializes the Node.js environment version of the
|
|
||||||
* platform-dependent connection object for the plugin site
|
|
||||||
*/
|
|
||||||
|
|
||||||
application = {};
|
|
||||||
connection = {};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints error message and its stack
|
|
||||||
*
|
|
||||||
* @param {Object} msg stack provided by error.stack or a message
|
|
||||||
*/
|
|
||||||
var printError = function(msg) {
|
|
||||||
console.error();
|
|
||||||
console.error(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event lisener for the plugin message
|
|
||||||
*/
|
|
||||||
process.on('message', function(m) {
|
|
||||||
switch(m.type){
|
|
||||||
case 'import':
|
|
||||||
importScript(m.url);
|
|
||||||
break;
|
|
||||||
case 'importJailed':
|
|
||||||
importScriptJailed(m.url);
|
|
||||||
break;
|
|
||||||
case 'execute':
|
|
||||||
execute(m.code);
|
|
||||||
break;
|
|
||||||
case 'message':
|
|
||||||
// unhandled exception would break the IPC channel
|
|
||||||
try {
|
|
||||||
conn._messageHandler(m.data);
|
|
||||||
} catch(e) {
|
|
||||||
printError(e.stack);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the given path is remote
|
|
||||||
*
|
|
||||||
* @param {String} path to check
|
|
||||||
* @returns {Boolean} true if path is remote
|
|
||||||
*/
|
|
||||||
var isRemote = function(path) {
|
|
||||||
return (path.substr(0,7).toLowerCase() == 'http://' ||
|
|
||||||
path.substr(0,8).toLowerCase() == 'https://');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads and executes the JavaScript file with the given url
|
|
||||||
*
|
|
||||||
* @param {String} url of the script to load
|
|
||||||
*/
|
|
||||||
var importScript = function(url) {
|
|
||||||
var sCb = function() {
|
|
||||||
process.send({type: 'importSuccess', url: url});
|
|
||||||
}
|
|
||||||
|
|
||||||
var fCb = function() {
|
|
||||||
process.send({type: 'importFailure', url: url});
|
|
||||||
}
|
|
||||||
|
|
||||||
var run = function(code) {
|
|
||||||
executeNormal(code, url, sCb, fCb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRemote(url)) {
|
|
||||||
loadRemote(url, run, fCb);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
run(loadLocal(url));
|
|
||||||
} catch(e) {
|
|
||||||
printError(e.stack);
|
|
||||||
fCb();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads and executes the JavaScript file with the given url in a
|
|
||||||
* jailed environment
|
|
||||||
*
|
|
||||||
* @param {String} url of the script to load
|
|
||||||
*/
|
|
||||||
var importScriptJailed = function(url) {
|
|
||||||
var sCb = function() {
|
|
||||||
process.send({type: 'importSuccess', url: url});
|
|
||||||
}
|
|
||||||
|
|
||||||
var fCb = function() {
|
|
||||||
process.send({type: 'importFailure', url: url});
|
|
||||||
}
|
|
||||||
|
|
||||||
var run = function(code) {
|
|
||||||
executeJailed(code, url, sCb, fCb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRemote(url)) {
|
|
||||||
loadRemote(url, run, fCb);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
run(loadLocal(url));
|
|
||||||
} catch (e) {
|
|
||||||
printError(e.stack);
|
|
||||||
fCb();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the given code in the jailed environment, sends the
|
|
||||||
* corresponding message to the application site when succeeded/failed
|
|
||||||
*
|
|
||||||
* @param {String} code to execute
|
|
||||||
*/
|
|
||||||
var execute = function(code) {
|
|
||||||
var sCb = function() {
|
|
||||||
process.send({type: 'executeSuccess'});
|
|
||||||
}
|
|
||||||
|
|
||||||
var fCb = function() {
|
|
||||||
process.send({type: 'executeFailure'});
|
|
||||||
}
|
|
||||||
|
|
||||||
executeJailed(code, 'DYNAMIC PLUGIN', sCb, fCb);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the given code in the current environment / scope, runs
|
|
||||||
* the corresponding callback when done
|
|
||||||
*
|
|
||||||
* @param {String} code to execute
|
|
||||||
* @param {String} url of the script (for displaying the stack)
|
|
||||||
* @param {Function} sCb
|
|
||||||
* @param {Function} fCb
|
|
||||||
*/
|
|
||||||
var executeNormal = function(code, url, sCb, fCb) {
|
|
||||||
var err = null;
|
|
||||||
try {
|
|
||||||
require('vm').runInThisContext(code, url);
|
|
||||||
sCb();
|
|
||||||
} catch (e) {
|
|
||||||
printError(e.stack);
|
|
||||||
fCb();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the given code in a jailed environment, runs the
|
|
||||||
* corresponding callback when done
|
|
||||||
*
|
|
||||||
* @param {String} code to execute
|
|
||||||
* @param {String} url of the script (for displaying the stack)
|
|
||||||
* @param {Function} sCb
|
|
||||||
* @param {Function} fCb
|
|
||||||
*/
|
|
||||||
var executeJailed = function(code, url, sCb, fCb) {
|
|
||||||
var vm = require('vm');
|
|
||||||
var sandbox = {};
|
|
||||||
var expose = [
|
|
||||||
'application',
|
|
||||||
'setTimeout',
|
|
||||||
'setInterval',
|
|
||||||
'clearTimeout',
|
|
||||||
'clearInterval'
|
|
||||||
];
|
|
||||||
|
|
||||||
for (var i = 0; i < expose.length; i++) {
|
|
||||||
sandbox[expose[i]] = global[expose[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
code = '"use strict";\n'+code;
|
|
||||||
try {
|
|
||||||
vm.runInNewContext(code, vm.createContext(sandbox), url);
|
|
||||||
sCb();
|
|
||||||
} catch (e) {
|
|
||||||
printError(e.stack);
|
|
||||||
fCb();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads local file and
|
|
||||||
*
|
|
||||||
* @param {String} path of the file to read
|
|
||||||
*
|
|
||||||
* @returns {String} file contents
|
|
||||||
*/
|
|
||||||
var loadLocal = function(path) {
|
|
||||||
return require("fs").readFileSync(path).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Downloads the script by remote url and provides its content as a
|
|
||||||
* string to the callback
|
|
||||||
*
|
|
||||||
* @param {String} url of the remote module to load
|
|
||||||
* @param {Function} sCb success callback
|
|
||||||
* @param {Function} fCb failure callback
|
|
||||||
*/
|
|
||||||
var loadRemote = function(url, sCb, fCb) {
|
|
||||||
var receive = function(res) {
|
|
||||||
if (res.statusCode != 200) {
|
|
||||||
var msg = 'Failed to load ' + url + '\n' +
|
|
||||||
'HTTP responce status code: ' + res.statusCode;
|
|
||||||
printError(msg);
|
|
||||||
fCb();
|
|
||||||
} else {
|
|
||||||
var content = '';
|
|
||||||
res.on('end', function(){ sCb(content); });
|
|
||||||
res.on(
|
|
||||||
'readable',
|
|
||||||
function() {
|
|
||||||
var chunk = res.read();
|
|
||||||
content += chunk.toString();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
require('http').get(url, receive).on('error', fCb);
|
|
||||||
} catch (e) {
|
|
||||||
printError(e.stack);
|
|
||||||
fCb();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connection object provided to the SandboxedSite constructor, plugin
|
|
||||||
* site implementation for the Node.js environment
|
|
||||||
*/
|
|
||||||
var conn = {
|
|
||||||
disconnect: function(){ process.exit(); },
|
|
||||||
send: function(data) {
|
|
||||||
process.send({type: 'message', data: data});
|
|
||||||
},
|
|
||||||
onMessage: function(h){ conn._messageHandler = h; },
|
|
||||||
_messageHandler: function(){},
|
|
||||||
onDisconnect: function() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
connection = conn;
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
|||||||
|
|
||||||
/**
|
|
||||||
* Contains the routines loaded by the plugin Worker under web-browser.
|
|
||||||
*
|
|
||||||
* Initializes the web environment version of the platform-dependent
|
|
||||||
* connection object for the plugin site
|
|
||||||
*/
|
|
||||||
|
|
||||||
self.application = {};
|
|
||||||
self.connection = {};
|
|
||||||
|
|
||||||
|
|
||||||
(function(){
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event lisener for the plugin message
|
|
||||||
*/
|
|
||||||
self.addEventListener('message', function(e){
|
|
||||||
var m = e.data.data;
|
|
||||||
switch (m.type) {
|
|
||||||
case 'import':
|
|
||||||
case 'importJailed': // already jailed in the Worker
|
|
||||||
importScript(m.url);
|
|
||||||
break;
|
|
||||||
case 'execute':
|
|
||||||
execute(m.code);
|
|
||||||
break;
|
|
||||||
case 'message':
|
|
||||||
conn._messageHandler(m.data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads and executes the JavaScript file with the given url
|
|
||||||
*
|
|
||||||
* @param {String} url to load
|
|
||||||
*/
|
|
||||||
var importScript = function(url) {
|
|
||||||
var error = null;
|
|
||||||
try {
|
|
||||||
importScripts(url);
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
self.postMessage({type: 'importFailure', url: url});
|
|
||||||
throw error;
|
|
||||||
} else {
|
|
||||||
self.postMessage({type: 'importSuccess', url: url});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the given code in a jailed environment. For web
|
|
||||||
* implementation, we're already jailed in the worker, so simply
|
|
||||||
* eval()
|
|
||||||
*
|
|
||||||
* @param {String} code code to execute
|
|
||||||
*/
|
|
||||||
var execute = function(code) {
|
|
||||||
try {
|
|
||||||
eval(code);
|
|
||||||
} catch (e) {
|
|
||||||
self.postMessage({type: 'executeFailure'});
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.postMessage({type: 'executeSuccess'});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connection object provided to the JailedSite constructor,
|
|
||||||
* plugin site implementation for the web-based environment.
|
|
||||||
* Global will be then cleared to prevent exposure into the
|
|
||||||
* Worker, so we put this local connection object into a closure
|
|
||||||
*/
|
|
||||||
var conn = {
|
|
||||||
disconnect: function(){ self.close(); },
|
|
||||||
send: function(data) {
|
|
||||||
self.postMessage({type: 'message', data: data});
|
|
||||||
},
|
|
||||||
onMessage: function(h){ conn._messageHandler = h; },
|
|
||||||
_messageHandler: function(){},
|
|
||||||
onDisconnect: function() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
connection = conn;
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
@ -1,780 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileoverview Jailed - safe yet flexible sandbox
|
|
||||||
* @version 0.2.0
|
|
||||||
*
|
|
||||||
* @license MIT, see http://github.com/asvd/jailed
|
|
||||||
* Copyright (c) 2014 asvd <heliosframework@gmail.com>
|
|
||||||
*
|
|
||||||
* Main library script, the only one to be loaded by a developer into
|
|
||||||
* the application. Other scrips shipped along will be loaded by the
|
|
||||||
* library either here (application site), or into the plugin site
|
|
||||||
* (Worker/child process):
|
|
||||||
*
|
|
||||||
* _JailedSite.js loaded into both applicaiton and plugin sites
|
|
||||||
* _frame.html sandboxed frame (web)
|
|
||||||
* _frame.js sandboxed frame code (web)
|
|
||||||
* _pluginWeb.js platform-dependent plugin routines (web)
|
|
||||||
* _pluginNode.js platform-dependent plugin routines (Node.js)
|
|
||||||
* _pluginCore.js common plugin site protocol implementation
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
var __jailed__path__;
|
|
||||||
if (typeof window == 'undefined') {
|
|
||||||
// Node.js
|
|
||||||
__jailed__path__ = __dirname + '/';
|
|
||||||
} else {
|
|
||||||
// web
|
|
||||||
var scripts = document.getElementsByTagName('script');
|
|
||||||
__jailed__path__ = scripts[scripts.length-1].src
|
|
||||||
.split('?')[0]
|
|
||||||
.split('/')
|
|
||||||
.slice(0, -1)
|
|
||||||
.join('/')+'/';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
(function (root, factory) {
|
|
||||||
if (typeof define === 'function' && define.amd) {
|
|
||||||
define(['exports'], factory);
|
|
||||||
} else if (typeof exports !== 'undefined') {
|
|
||||||
factory(exports);
|
|
||||||
} else {
|
|
||||||
factory((root.jailed = {}));
|
|
||||||
}
|
|
||||||
}(this, function (exports) {
|
|
||||||
var isNode = typeof window == 'undefined';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A special kind of event:
|
|
||||||
* - which can only be emitted once;
|
|
||||||
* - executes a set of subscribed handlers upon emission;
|
|
||||||
* - if a handler is subscribed after the event was emitted, it
|
|
||||||
* will be invoked immideately.
|
|
||||||
*
|
|
||||||
* Used for the events which only happen once (or do not happen at
|
|
||||||
* all) during a single plugin lifecycle - connect, disconnect and
|
|
||||||
* connection failure
|
|
||||||
*/
|
|
||||||
var Whenable = function() {
|
|
||||||
this._emitted = false;
|
|
||||||
this._handlers = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emits the Whenable event, calls all the handlers already
|
|
||||||
* subscribed, switches the object to the 'emitted' state (when
|
|
||||||
* all future subscibed listeners will be immideately issued
|
|
||||||
* instead of being stored)
|
|
||||||
*/
|
|
||||||
Whenable.prototype.emit = function(){
|
|
||||||
if (!this._emitted) {
|
|
||||||
this._emitted = true;
|
|
||||||
|
|
||||||
var handler;
|
|
||||||
while(handler = this._handlers.pop()) {
|
|
||||||
setTimeout(handler,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the provided function as a handler for the Whenable
|
|
||||||
* event. This handler will then be called upon the event emission
|
|
||||||
* (if it has not been emitted yet), or will be scheduled for
|
|
||||||
* immediate issue (if the event has already been emmitted before)
|
|
||||||
*
|
|
||||||
* @param {Function} handler to subscribe for the event
|
|
||||||
*/
|
|
||||||
Whenable.prototype.whenEmitted = function(handler){
|
|
||||||
handler = this._checkHandler(handler);
|
|
||||||
if (this._emitted) {
|
|
||||||
setTimeout(handler, 0);
|
|
||||||
} else {
|
|
||||||
this._handlers.push(handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the provided object is suitable for being subscribed
|
|
||||||
* to the event (= is a function), throws an exception if not
|
|
||||||
*
|
|
||||||
* @param {Object} obj to check for being subscribable
|
|
||||||
*
|
|
||||||
* @throws {Exception} if object is not suitable for subscription
|
|
||||||
*
|
|
||||||
* @returns {Object} the provided object if yes
|
|
||||||
*/
|
|
||||||
Whenable.prototype._checkHandler = function(handler){
|
|
||||||
var type = typeof handler;
|
|
||||||
if (type != 'function') {
|
|
||||||
var msg =
|
|
||||||
'A function may only be subsribed to the event, '
|
|
||||||
+ type
|
|
||||||
+ ' was provided instead'
|
|
||||||
throw new Error(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the library site for Node.js environment (loads
|
|
||||||
* _JailedSite.js)
|
|
||||||
*/
|
|
||||||
var initNode = function() {
|
|
||||||
require('./_JailedSite.js');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the library site for web environment (loads
|
|
||||||
* _JailedSite.js)
|
|
||||||
*/
|
|
||||||
var platformInit;
|
|
||||||
var initWeb = function() {
|
|
||||||
// loads additional script to the application environment
|
|
||||||
var load = function(path, cb) {
|
|
||||||
var script = document.createElement('script');
|
|
||||||
script.src = path;
|
|
||||||
|
|
||||||
var clear = function() {
|
|
||||||
script.onload = null;
|
|
||||||
script.onerror = null;
|
|
||||||
script.onreadystatechange = null;
|
|
||||||
script.parentNode.removeChild(script);
|
|
||||||
}
|
|
||||||
|
|
||||||
var success = function() {
|
|
||||||
clear();
|
|
||||||
cb();
|
|
||||||
}
|
|
||||||
|
|
||||||
script.onerror = clear;
|
|
||||||
script.onload = success;
|
|
||||||
script.onreadystatechange = function() {
|
|
||||||
var state = script.readyState;
|
|
||||||
if (state==='loaded' || state==='complete') {
|
|
||||||
success();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.appendChild(script);
|
|
||||||
}
|
|
||||||
|
|
||||||
platformInit = new Whenable;
|
|
||||||
var origOnload = window.onload || function(){};
|
|
||||||
|
|
||||||
window.onload = function(){
|
|
||||||
origOnload();
|
|
||||||
load(
|
|
||||||
__jailed__path__+'_JailedSite.js',
|
|
||||||
function(){ platformInit.emit(); }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var BasicConnection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the platform-dependent BasicConnection object in the
|
|
||||||
* Node.js environment
|
|
||||||
*/
|
|
||||||
var basicConnectionNode = function() {
|
|
||||||
var childProcess = require('child_process');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Platform-dependent implementation of the BasicConnection
|
|
||||||
* object, initializes the plugin site and provides the basic
|
|
||||||
* messaging-based connection with it
|
|
||||||
*
|
|
||||||
* For Node.js the plugin is created as a forked process
|
|
||||||
*/
|
|
||||||
BasicConnection = function() {
|
|
||||||
this._disconnected = false;
|
|
||||||
this._messageHandler = function(){};
|
|
||||||
this._disconnectHandler = function(){};
|
|
||||||
this._process = childProcess.fork(
|
|
||||||
__jailed__path__+'_pluginNode.js'
|
|
||||||
);
|
|
||||||
|
|
||||||
var me = this;
|
|
||||||
this._process.on('message', function(m){
|
|
||||||
me._messageHandler(m);
|
|
||||||
});
|
|
||||||
|
|
||||||
this._process.on('exit', function(m){
|
|
||||||
me._disconnected = true;
|
|
||||||
me._disconnectHandler(m);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets-up the handler to be called upon the BasicConnection
|
|
||||||
* initialization is completed.
|
|
||||||
*
|
|
||||||
* For Node.js the connection is fully initialized within the
|
|
||||||
* constructor, so simply calls the provided handler.
|
|
||||||
*
|
|
||||||
* @param {Function} handler to be called upon connection init
|
|
||||||
*/
|
|
||||||
BasicConnection.prototype.whenInit = function(handler) {
|
|
||||||
handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a message to the plugin site
|
|
||||||
*
|
|
||||||
* @param {Object} data to send
|
|
||||||
*/
|
|
||||||
BasicConnection.prototype.send = function(data) {
|
|
||||||
if (!this._disconnected) {
|
|
||||||
this._process.send(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a handler for a message received from the plugin site
|
|
||||||
*
|
|
||||||
* @param {Function} handler to call upon a message
|
|
||||||
*/
|
|
||||||
BasicConnection.prototype.onMessage = function(handler) {
|
|
||||||
this._messageHandler = function(data) {
|
|
||||||
// broken stack would break the IPC in Node.js
|
|
||||||
try {
|
|
||||||
handler(data);
|
|
||||||
} catch (e) {
|
|
||||||
console.error();
|
|
||||||
console.error(e.stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a handler for the event of plugin disconnection
|
|
||||||
* (= plugin process exit)
|
|
||||||
*
|
|
||||||
* @param {Function} handler to call upon a disconnect
|
|
||||||
*/
|
|
||||||
BasicConnection.prototype.onDisconnect = function(handler) {
|
|
||||||
this._disconnectHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnects the plugin (= kills the forked process)
|
|
||||||
*/
|
|
||||||
BasicConnection.prototype.disconnect = function() {
|
|
||||||
this._process.kill('SIGKILL');
|
|
||||||
this._disconnected = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the platform-dependent BasicConnection object in the
|
|
||||||
* web-browser environment
|
|
||||||
*/
|
|
||||||
var basicConnectionWeb = function() {
|
|
||||||
var perm = ['allow-scripts'];
|
|
||||||
|
|
||||||
if (__jailed__path__.substr(0,7).toLowerCase() == 'file://') {
|
|
||||||
// local instance requires extra permission
|
|
||||||
perm.push('allow-same-origin');
|
|
||||||
}
|
|
||||||
|
|
||||||
// frame element to be cloned
|
|
||||||
var sample = document.createElement('iframe');
|
|
||||||
sample.src = __jailed__path__ + '_frame.html';
|
|
||||||
sample.sandbox = perm.join(' ');
|
|
||||||
sample.style.display = 'none';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Platform-dependent implementation of the BasicConnection
|
|
||||||
* object, initializes the plugin site and provides the basic
|
|
||||||
* messaging-based connection with it
|
|
||||||
*
|
|
||||||
* For the web-browser environment, the plugin is created as a
|
|
||||||
* Worker in a sandbaxed frame
|
|
||||||
*/
|
|
||||||
BasicConnection = function() {
|
|
||||||
this._init = new Whenable;
|
|
||||||
this._disconnected = false;
|
|
||||||
|
|
||||||
var me = this;
|
|
||||||
platformInit.whenEmitted(function() {
|
|
||||||
if (!me._disconnected) {
|
|
||||||
me._frame = sample.cloneNode(false);
|
|
||||||
document.body.appendChild(me._frame);
|
|
||||||
|
|
||||||
window.addEventListener('message', function (e) {
|
|
||||||
if (e.origin === "null" &&
|
|
||||||
e.source === me._frame.contentWindow) {
|
|
||||||
if (e.data.type == 'initialized') {
|
|
||||||
me._init.emit();
|
|
||||||
} else {
|
|
||||||
me._messageHandler(e.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets-up the handler to be called upon the BasicConnection
|
|
||||||
* initialization is completed.
|
|
||||||
*
|
|
||||||
* For the web-browser environment, the handler is issued when
|
|
||||||
* the plugin worker successfully imported and executed the
|
|
||||||
* _pluginWeb.js, and replied to the application site with the
|
|
||||||
* initImprotSuccess message.
|
|
||||||
*
|
|
||||||
* @param {Function} handler to be called upon connection init
|
|
||||||
*/
|
|
||||||
BasicConnection.prototype.whenInit = function(handler) {
|
|
||||||
this._init.whenEmitted(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a message to the plugin site
|
|
||||||
*
|
|
||||||
* @param {Object} data to send
|
|
||||||
*/
|
|
||||||
BasicConnection.prototype.send = function(data) {
|
|
||||||
this._frame.contentWindow.postMessage(
|
|
||||||
{type: 'message', data: data}, '*'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a handler for a message received from the plugin site
|
|
||||||
*
|
|
||||||
* @param {Function} handler to call upon a message
|
|
||||||
*/
|
|
||||||
BasicConnection.prototype.onMessage = function(handler) {
|
|
||||||
this._messageHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a handler for the event of plugin disconnection
|
|
||||||
* (not used in case of Worker)
|
|
||||||
*
|
|
||||||
* @param {Function} handler to call upon a disconnect
|
|
||||||
*/
|
|
||||||
BasicConnection.prototype.onDisconnect = function(){};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnects the plugin (= kills the frame)
|
|
||||||
*/
|
|
||||||
BasicConnection.prototype.disconnect = function() {
|
|
||||||
if (!this._disconnected) {
|
|
||||||
this._disconnected = true;
|
|
||||||
if (typeof this._frame != 'undefined') {
|
|
||||||
this._frame.parentNode.removeChild(this._frame);
|
|
||||||
} // otherwise farme is not yet created
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (isNode) {
|
|
||||||
initNode();
|
|
||||||
basicConnectionNode();
|
|
||||||
} else {
|
|
||||||
initWeb();
|
|
||||||
basicConnectionWeb();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Application-site Connection object constructon, reuses the
|
|
||||||
* platform-dependent BasicConnection declared above in order to
|
|
||||||
* communicate with the plugin environment, implements the
|
|
||||||
* application-site protocol of the interraction: provides some
|
|
||||||
* methods for loading scripts and executing the given code in the
|
|
||||||
* plugin
|
|
||||||
*/
|
|
||||||
var Connection = function(){
|
|
||||||
this._platformConnection = new BasicConnection;
|
|
||||||
|
|
||||||
this._importCallbacks = {};
|
|
||||||
this._executeSCb = function(){};
|
|
||||||
this._executeFCb = function(){};
|
|
||||||
this._messageHandler = function(){};
|
|
||||||
|
|
||||||
var me = this;
|
|
||||||
this.whenInit = function(cb){
|
|
||||||
me._platformConnection.whenInit(cb);
|
|
||||||
};
|
|
||||||
|
|
||||||
this._platformConnection.onMessage(function(m) {
|
|
||||||
switch(m.type) {
|
|
||||||
case 'message':
|
|
||||||
me._messageHandler(m.data);
|
|
||||||
break;
|
|
||||||
case 'importSuccess':
|
|
||||||
me._handleImportSuccess(m.url);
|
|
||||||
break;
|
|
||||||
case 'importFailure':
|
|
||||||
me._handleImportFailure(m.url);
|
|
||||||
break;
|
|
||||||
case 'executeSuccess':
|
|
||||||
me._executeSCb();
|
|
||||||
break;
|
|
||||||
case 'executeFailure':
|
|
||||||
me._executeFCb();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the plugin to load a script with the given path, and to
|
|
||||||
* execute it. Callbacks executed upon the corresponding responce
|
|
||||||
* message from the plugin site
|
|
||||||
*
|
|
||||||
* @param {String} path of a script to load
|
|
||||||
* @param {Function} sCb to call upon success
|
|
||||||
* @param {Function} fCb to call upon failure
|
|
||||||
*/
|
|
||||||
Connection.prototype.importScript = function(path, sCb, fCb) {
|
|
||||||
var f = function(){};
|
|
||||||
this._importCallbacks[path] = {sCb: sCb||f, fCb: fCb||f};
|
|
||||||
this._platformConnection.send({type: 'import', url: path});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the plugin to load a script with the given path, and to
|
|
||||||
* execute it in the JAILED environment. Callbacks executed upon
|
|
||||||
* the corresponding responce message from the plugin site
|
|
||||||
*
|
|
||||||
* @param {String} path of a script to load
|
|
||||||
* @param {Function} sCb to call upon success
|
|
||||||
* @param {Function} fCb to call upon failure
|
|
||||||
*/
|
|
||||||
Connection.prototype.importJailedScript = function(path, sCb, fCb) {
|
|
||||||
var f = function(){};
|
|
||||||
this._importCallbacks[path] = {sCb: sCb||f, fCb: fCb||f};
|
|
||||||
this._platformConnection.send({type: 'importJailed', url: path});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the code to the plugin site in order to have it executed
|
|
||||||
* in the JAILED enviroment. Assuming the execution may only be
|
|
||||||
* requested once by the Plugin object, which means a single set
|
|
||||||
* of callbacks is enough (unlike importing additional scripts)
|
|
||||||
*
|
|
||||||
* @param {String} code code to execute
|
|
||||||
* @param {Function} sCb to call upon success
|
|
||||||
* @param {Function} fCb to call upon failure
|
|
||||||
*/
|
|
||||||
Connection.prototype.execute = function(code, sCb, fCb) {
|
|
||||||
this._executeSCb = sCb||function(){};
|
|
||||||
this._executeFCb = fCb||function(){};
|
|
||||||
this._platformConnection.send({type: 'execute', code: code});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a handler for a message received from the plugin site
|
|
||||||
*
|
|
||||||
* @param {Function} handler to call upon a message
|
|
||||||
*/
|
|
||||||
Connection.prototype.onMessage = function(handler) {
|
|
||||||
this._messageHandler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a handler for a disconnect message received from the
|
|
||||||
* plugin site
|
|
||||||
*
|
|
||||||
* @param {Function} handler to call upon disconnect
|
|
||||||
*/
|
|
||||||
Connection.prototype.onDisconnect = function(handler) {
|
|
||||||
this._platformConnection.onDisconnect(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a message to the plugin
|
|
||||||
*
|
|
||||||
* @param {Object} data of the message to send
|
|
||||||
*/
|
|
||||||
Connection.prototype.send = function(data) {
|
|
||||||
this._platformConnection.send({
|
|
||||||
type: 'message',
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles import succeeded message from the plugin
|
|
||||||
*
|
|
||||||
* @param {String} url of a script loaded by the plugin
|
|
||||||
*/
|
|
||||||
Connection.prototype._handleImportSuccess = function(url) {
|
|
||||||
var sCb = this._importCallbacks[url].sCb;
|
|
||||||
this._importCallbacks[url] = null;
|
|
||||||
delete this._importCallbacks[url];
|
|
||||||
sCb();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles import failure message from the plugin
|
|
||||||
*
|
|
||||||
* @param {String} url of a script loaded by the plugin
|
|
||||||
*/
|
|
||||||
Connection.prototype._handleImportFailure = function(url) {
|
|
||||||
var fCb = this._importCallbacks[url].fCb;
|
|
||||||
this._importCallbacks[url] = null;
|
|
||||||
delete this._importCallbacks[url];
|
|
||||||
fCb();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnects the plugin when it is not needed anymore
|
|
||||||
*/
|
|
||||||
Connection.prototype.disconnect = function() {
|
|
||||||
this._platformConnection.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plugin constructor, represents a plugin initialized by a script
|
|
||||||
* with the given path
|
|
||||||
*
|
|
||||||
* @param {String} url of a plugin source
|
|
||||||
* @param {Object} _interface to provide for the plugin
|
|
||||||
*/
|
|
||||||
var Plugin = function(url, _interface) {
|
|
||||||
this._path = url;
|
|
||||||
this._initialInterface = _interface||{};
|
|
||||||
this._connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DynamicPlugin constructor, represents a plugin initialized by a
|
|
||||||
* string containing the code to be executed
|
|
||||||
*
|
|
||||||
* @param {String} code of the plugin
|
|
||||||
* @param {Object} _interface to provide to the plugin
|
|
||||||
*/
|
|
||||||
var DynamicPlugin = function(code, _interface) {
|
|
||||||
this._code = code;
|
|
||||||
this._initialInterface = _interface||{};
|
|
||||||
this._connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the connection to the plugin site
|
|
||||||
*/
|
|
||||||
DynamicPlugin.prototype._connect =
|
|
||||||
Plugin.prototype._connect = function() {
|
|
||||||
this.remote = null;
|
|
||||||
|
|
||||||
this._connect = new Whenable;
|
|
||||||
this._fail = new Whenable;
|
|
||||||
this._disconnect = new Whenable;
|
|
||||||
|
|
||||||
var me = this;
|
|
||||||
|
|
||||||
// binded failure callback
|
|
||||||
this._fCb = function(){
|
|
||||||
me._fail.emit();
|
|
||||||
me.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._connection = new Connection;
|
|
||||||
this._connection.whenInit(function(){
|
|
||||||
me._init();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the Site object for the plugin, and then loads the
|
|
||||||
* common routines (_JailedSite.js)
|
|
||||||
*/
|
|
||||||
DynamicPlugin.prototype._init =
|
|
||||||
Plugin.prototype._init = function() {
|
|
||||||
this._site = new JailedSite(this._connection);
|
|
||||||
|
|
||||||
var me = this;
|
|
||||||
this._site.onDisconnect(function() {
|
|
||||||
me._disconnect.emit();
|
|
||||||
});
|
|
||||||
|
|
||||||
var sCb = function() {
|
|
||||||
me._loadCore();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._connection.importScript(
|
|
||||||
__jailed__path__+'_JailedSite.js', sCb, this._fCb
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the core scirpt into the plugin
|
|
||||||
*/
|
|
||||||
DynamicPlugin.prototype._loadCore =
|
|
||||||
Plugin.prototype._loadCore = function() {
|
|
||||||
var me = this;
|
|
||||||
var sCb = function() {
|
|
||||||
me._sendInterface();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._connection.importScript(
|
|
||||||
__jailed__path__+'_pluginCore.js', sCb, this._fCb
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends to the remote site a signature of the interface provided
|
|
||||||
* upon the Plugin creation
|
|
||||||
*/
|
|
||||||
DynamicPlugin.prototype._sendInterface =
|
|
||||||
Plugin.prototype._sendInterface = function() {
|
|
||||||
var me = this;
|
|
||||||
this._site.onInterfaceSetAsRemote(function() {
|
|
||||||
if (!me._connected) {
|
|
||||||
me._loadPlugin();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this._site.setInterface(this._initialInterface);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the plugin body (loads the plugin url in case of the
|
|
||||||
* Plugin)
|
|
||||||
*/
|
|
||||||
Plugin.prototype._loadPlugin = function() {
|
|
||||||
var me = this;
|
|
||||||
var sCb = function() {
|
|
||||||
me._requestRemote();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._connection.importJailedScript(this._path, sCb, this._fCb);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the plugin body (executes the code in case of the
|
|
||||||
* DynamicPlugin)
|
|
||||||
*/
|
|
||||||
DynamicPlugin.prototype._loadPlugin = function() {
|
|
||||||
var me = this;
|
|
||||||
var sCb = function() {
|
|
||||||
me._requestRemote();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._connection.execute(this._code, sCb, this._fCb);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests the remote interface from the plugin (which was
|
|
||||||
* probably set by the plugin during its initialization), emits
|
|
||||||
* the connect event when done, then the plugin is fully usable
|
|
||||||
* (meaning both the plugin and the application can use the
|
|
||||||
* interfaces provided to each other)
|
|
||||||
*/
|
|
||||||
DynamicPlugin.prototype._requestRemote =
|
|
||||||
Plugin.prototype._requestRemote = function() {
|
|
||||||
var me = this;
|
|
||||||
this._site.onRemoteUpdate(function(){
|
|
||||||
me.remote = me._site.getRemote();
|
|
||||||
me._connect.emit();
|
|
||||||
});
|
|
||||||
|
|
||||||
this._site.requestRemote();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnects the plugin immideately
|
|
||||||
*/
|
|
||||||
DynamicPlugin.prototype.disconnect =
|
|
||||||
Plugin.prototype.disconnect = function() {
|
|
||||||
this._connection.disconnect();
|
|
||||||
this._disconnect.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the provided function as a handler for the connection
|
|
||||||
* failure Whenable event
|
|
||||||
*
|
|
||||||
* @param {Function} handler to be issued upon disconnect
|
|
||||||
*/
|
|
||||||
DynamicPlugin.prototype.whenFailed =
|
|
||||||
Plugin.prototype.whenFailed = function(handler) {
|
|
||||||
this._fail.whenEmitted(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the provided function as a handler for the connection
|
|
||||||
* success Whenable event
|
|
||||||
*
|
|
||||||
* @param {Function} handler to be issued upon connection
|
|
||||||
*/
|
|
||||||
DynamicPlugin.prototype.whenConnected =
|
|
||||||
Plugin.prototype.whenConnected = function(handler) {
|
|
||||||
this._connect.whenEmitted(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the provided function as a handler for the connection
|
|
||||||
* failure Whenable event
|
|
||||||
*
|
|
||||||
* @param {Function} handler to be issued upon connection failure
|
|
||||||
*/
|
|
||||||
DynamicPlugin.prototype.whenDisconnected =
|
|
||||||
Plugin.prototype.whenDisconnected = function(handler) {
|
|
||||||
this._disconnect.whenEmitted(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
exports.Plugin = Plugin;
|
|
||||||
exports.DynamicPlugin = DynamicPlugin;
|
|
||||||
|
|
||||||
}));
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
|||||||
script(type='text/javascript', src='/js/lib/jailed/jailed.js')
|
|
||||||
script(src=rev('/js', 'vendor-challenges.js'))
|
script(src=rev('/js', 'vendor-challenges.js'))
|
||||||
script(src=rev('/js', 'commonFramework.js'))
|
script(src=rev('/js', 'commonFramework.js'))
|
||||||
script.
|
script.
|
||||||
|
Reference in New Issue
Block a user