blob: c87bc59eccb6b35da360d4c33d1b42e10a78f4b6 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
var domready = require('domready');
var EE = require('events').EventEmitter;
var inherits = require('inherits');
var Channel = require('./channel');
var QueuedRpcChannelWrapper = require('./queued_channel');
var state = require('../state');
// Clean the extension blessing whenever the extension is updated.
// See https://developer.chrome.com/extensions/runtime#event-onInstalled
var cleanBlessingsOnStart = false;
chrome.runtime.onInstalled.addListener(function(details) {
if (details.reason === 'update') {
cleanBlessingsOnStart = true;
}
});
module.exports = Nacl;
function Nacl() {
if (!(this instanceof Nacl)) {
return new Nacl();
}
EE.call(this);
this._queuedMessages = [];
// Wait until the dom is ready, then add 'load' and 'message' listeners on the
// nacl plugin that will trigger events on this object.
var nacl = this;
domready(function(){
nacl._naclElt = _createNaclElement();
document.body.appendChild(nacl._naclElt);
// 'load' listener must have useCapture argument set to 'true'.
nacl._naclElt.addEventListener('load', nacl.emit.bind(nacl, 'load'), true);
nacl._naclElt.addEventListener('crash', nacl.emit.bind(nacl, 'crash', null),
true);
nacl._naclElt.addEventListener('message', function(e) {
var msg = e.data;
if (msg instanceof ArrayBuffer) {
// Message is response from a channel RPC.
return nacl._directChannel.handleMessage(msg);
}
nacl.emit('message', msg);
});
// Channel for bi-directional RPCs.
nacl._directChannel = new Channel(nacl._naclElt.postMessage.bind(nacl));
nacl.channel = new QueuedRpcChannelWrapper(nacl._directChannel);
// Send a signal to initialize the nacl plug-in.
nacl._start();
});
}
inherits(Nacl, EE);
// Create an embed tag that will contain the nacl plugin.
function _createNaclElement() {
var naclElt = document.createElement('embed');
var idAttr = document.createAttribute('id');
idAttr.value = 'nacl';
naclElt.setAttributeNode(idAttr);
var srcAttr = document.createAttribute('src');
srcAttr.value = '/nacl/main.nmf';
naclElt.setAttributeNode(srcAttr);
var typeAttr = document.createAttribute('type');
typeAttr.value = 'application/x-nacl';
naclElt.setAttributeNode(typeAttr);
return naclElt;
}
// Send message from content script to Nacl.
Nacl.prototype.sendMessage = function(msg) {
if (!this._initialized) {
this._queuedMessages.push(msg);
return;
}
this._naclElt.postMessage(msg);
};
Nacl.prototype._start = function() {
var nacl = this;
var settings = state.settings();
console.log(settings);
this.getBlessingRoot(settings.identitydBlessingUrl.value,
function(err, identityBlessingRoot) {
if (err) {
return nacl.emit('crash', err.message);
}
var body = {
cleanupBlessings: cleanBlessingsOnStart,
identitydBlessingRoot: identityBlessingRoot,
identityd: settings.identityd.value,
namespaceRoot: settings.namespaceRoot.value,
proxy: settings.proxy.value,
logLevel: parseInt(settings.logLevel.value) || 0,
logModule: settings.logModule.value || ''
};
cleanBlessingsOnStart = false;
nacl._directChannel.performRpc('start', body, function(err) {
if (err) {
return nacl.emit('crash', err.message);
}
nacl._initialized = true;
nacl._sendQueuedMessages();
nacl.channel.ready();
nacl.isReady = true;
nacl.emit('ready');
});
});
};
Nacl.prototype._sendQueuedMessages = function() {
var nacl = this;
this._queuedMessages.forEach(function(msg) {
nacl._naclElt.postMessage(msg);
});
this._queuedMessages = [];
console.log('Sent queued messages');
};
// Cache blessind root so we don't fetch it every time we start NaCl.
var cachedBlessingRoot = null;
Nacl.prototype.getBlessingRoot = function(url, cb) {
if (cachedBlessingRoot) {
return process.nextTick(function() {
cb(null, cachedBlessingRoot);
});
}
console.log('Requesting blessing root from ' + url);
var request = require('superagent');
request
.get(url)
.accept('application/json')
.end(function(err, res) {
if (err) {
cb(err);
} else if (res.error) {
cb(new Error('' + res.status + ': ' + res.message));
} else {
cachedBlessingRoot = res.body;
cb(null, cachedBlessingRoot);
}
});
};
Nacl.prototype.cleanupInstance = function(instanceId, cb) {
this.channel.performRpc('cleanup', {
instanceId: instanceId
}, cb);
};
// Destroy state associated with this Nacl instance.
// In particular, this removed the added embed tag.
Nacl.prototype.destroy = function() {
this._naclElt.parentNode.removeChild(this._naclElt);
this._naclElt = null;
};