diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index 18836a0d7..ba0ab423f 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -704,26 +704,18 @@ vAPI.messaging.onMessage = function({target, data}) { .chromeEventHandler.ownerDocument.defaultView.messageManager; } - var listenerId = data.channelName.split('|'); - var requestId = data.requestId; - var channelName = listenerId[1]; - listenerId = listenerId[0]; + var channelNameRaw = data.channelName; + var pos = channelNameRaw.indexOf('|'); + var channelName = channelNameRaw.slice(pos + 1); var callback = vAPI.messaging.NOOPFUNC; - if ( requestId !== undefined ) { - callback = function(response) { - var message = JSON.stringify({ - requestId: requestId, - channelName: channelName, - msg: response !== undefined ? response : null - }); - - if ( messageManager.sendAsyncMessage ) { - messageManager.sendAsyncMessage(listenerId, message); - } else { - messageManager.broadcastAsyncMessage(listenerId, message); - } - }; + if ( data.requestId !== undefined ) { + callback = CallbackWrapper.factory( + messageManager, + channelName, + channelNameRaw.slice(0, pos), + data.requestId + ).callback; } var sender = { @@ -798,6 +790,67 @@ vAPI.messaging.broadcast = function(message) { /******************************************************************************/ +// This allows to avoid creating a closure for every single message which +// expects an answer. Having a closure created each time a message is processed +// has been always bothering me. Another benefit of the implementation here +// is to reuse the callback proxy object, so less memory churning. +// +// https://developers.google.com/speed/articles/optimizing-javascript +// "Creating a closure is significantly slower then creating an inner +// function without a closure, and much slower than reusing a static +// function" +// +// http://hacksoflife.blogspot.ca/2015/01/the-four-horsemen-of-performance.html +// "the dreaded 'uniformly slow code' case where every function takes 1% +// of CPU and you have to make one hundred separate performance optimizations +// to improve performance at all" + +var CallbackWrapper = function(messageManager, channelName, listenerId, requestId) { + this.callback = this.proxy.bind(this); // bind once + this.init(messageManager, channelName, listenerId, requestId); +}; + +CallbackWrapper.junkyard = []; + +CallbackWrapper.factory = function(messageManager, channelName, listenerId, requestId) { + var wrapper = CallbackWrapper.junkyard.pop(); + if ( wrapper ) { + wrapper.init(messageManager, channelName, listenerId, requestId); + return wrapper; + } + return new CallbackWrapper(messageManager, channelName, listenerId, requestId); +}; + +CallbackWrapper.prototype.init = function(messageManager, channelName, listenerId, requestId) { + this.messageManager = messageManager; + this.channelName = channelName; + this.listenerId = listenerId; + this.requestId = requestId; +}; + +CallbackWrapper.prototype.proxy = function(response) { + var message = JSON.stringify({ + requestId: this.requestId, + channelName: this.channelName, + msg: response !== undefined ? response : null + }); + + if ( this.messageManager.sendAsyncMessage ) { + this.messageManager.sendAsyncMessage(this.listenerId, message); + } else { + this.messageManager.broadcastAsyncMessage(this.listenerId, message); + } + + // Mark for reuse + this.messageManager = + this.channelName = + this.requestId = + this.listenerId = null; + CallbackWrapper.junkyard.push(this); +}; + +/******************************************************************************/ + var httpObserver = { classDescription: 'net-channel-event-sinks for ' + location.host, classID: Components.ID('{dc8d6319-5f6e-4438-999e-53722db99e84}'),