├── .gitignore ├── LICENSE.txt ├── README.md ├── build └── queuebert.min.js ├── example ├── background.html └── content-script.js ├── images └── queuebert.png ├── lib ├── background-client.js ├── client.js └── server.js ├── manifest.json ├── package.json └── test ├── background-client-test.js ├── client-test.js └── server-test.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Attachments.me 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Queuebert 2 | ========= 3 | 4 | ![Queuebert](https://github.com/attachmentsme/Queuebert/raw/master/images/queuebert.png) 5 | 6 | The Chrome extension paradigm is sandboxed. iframes and browser-tabs cannot directly communicate with each other. They must use a background script to mediate communication between them. 7 | 8 | In a prior post, I advocate using a queue-based approach to deal with this annoyance: 9 | 10 | https://github.com/bcoe/DoloresLabsTechTalk 11 | 12 | Queuebert is a library designed to simplify building Chrome extensions using a queue-based approach. 13 | 14 | Server 15 | ------ 16 | 17 | * You create one instance of a server in the background.html. 18 | * The server handles queuing up messages for the content scripts. 19 | 20 | ```javascript 21 | var server = new Queuebert.Server(); 22 | ``` 23 | 24 | Client 25 | ------ 26 | 27 | You create a client within your content scripts. The client is used to communicate between the sandboxed JavaScript environments. 28 | 29 | * The client is used to dispatch messages to other tabs, iframes, and to the background script. 30 | * The client has a delegate registered with it. The delegate receives messages from other clients in the system. 31 | 32 | ```javascript 33 | var client = new Queuebert.Client({ 34 | identifier: 'client', 35 | delegate: delegate 36 | }); 37 | ``` 38 | * _identifier_ a tab with multiple iframes within it can potentially run multiple clients. The _identifier_ is used to differentiate these clients. 39 | * _delegate_ the client automatically checks for inbound messages on a set interval. Messages are automatically dispatched to the delegate. 40 | 41 | *Example Delegate* 42 | 43 | ```javascript 44 | var delegate = { 45 | receivedMessage: function(clientId, clientTabId, body) { 46 | console.log(body.message); 47 | } 48 | }; 49 | ``` 50 | 51 | The _receivedMessage_ message method will be executed if the following message was dispatched by another client: 52 | 53 | ```javascript 54 | client.sendMessage({ 55 | action: 'receivedMessage', 56 | tabId: clientTabId, 57 | to: 'client', 58 | body: {message: 'a message'} 59 | }); 60 | ``` 61 | 62 | The _clientId_ and _clientTabId_ variables can be used to dispatch a message back to the originating client. The _body_ is the body of the message dispatched by the originating client. 63 | 64 | BackgroundClient 65 | ---------------- 66 | 67 | To keep the queue-based paradigm consistent, you can create an instance of a _BackgroundClient_ in your _background.html_. 68 | 69 | The behaviour of a background client is identical to clients in content scripts except, seeing as the background script has no _tab.id_, background clients have the special _tab.id_ _background_. 70 | 71 | *Creating a BackgroundClient* 72 | 73 | ```javascript 74 | var client = new Queuebert.BackgroundClient({ 75 | delegate: delegate, 76 | server: server 77 | }); 78 | ``` 79 | 80 | * _delegate_ the delegate provided to a background client is identical to a delegate provided to other clients. 81 | * _server_ a BackgroundClient requires that the _Server_ instance be provided as a dependency. 82 | 83 | *Sending a Message to a BackgroundClient* 84 | 85 | ```javascript 86 | client.sendMessage({ 87 | action: 'echo', 88 | tabId: 'background', 89 | to: 'background', 90 | body: {message: 'Hello World!'} 91 | }); 92 | ``` 93 | 94 | The Example 95 | ----------- 96 | 97 | In the _/example_ folder, there are examples of each of the concepts discussed. 98 | 99 | The _Queuebert_ project is a fully functional Chrome Extension. Install it, to see things in action. 100 | 101 | Testing 102 | ------- 103 | 104 | Queuebert uses node.js for unit testing. run _node test/index.js_ to execute the test suite. 105 | 106 | Copyright 107 | --------- 108 | 109 | Copyright (c) 2011 Attachments.me. See LICENSE.txt for further details. -------------------------------------------------------------------------------- /build/queuebert.min.js: -------------------------------------------------------------------------------- 1 | if(typeof(Queuebert)==="undefined"){Queuebert={}}(function(){function a(b){var c=b||{};this.delegate=c.delegate||{};this.identifier=c.identifier;this.messageInterval=c.messageInterval||150;this.sendRequest=c.sendRequest||chrome.extension.sendRequest;this.setInterval=c.setInterval||function(e,d){setInterval(e,d)};if(c.start===undefined||c.start){this.start()}}a.prototype.start=function(){var b=this;this.setInterval(function(){b.sendRequest({from:b.identifier,type:"get"},function(c){if(c){b.handleMessage(c.action,c.from,c.tabId,c.body,c.callback)}})},this.messageInterval)};a.prototype.handleMessage=function(e,c,d,b,f){if(this.delegate[e]){this.delegate[e](c,d,b,f)}};a.prototype.sendMessage=function(c,d){var b={tabId:c.tabId,to:c.to,from:this.identifier,action:c.action,type:"put",body:c.body};d?this.sendRequest(b,d):this.sendRequest(b)};Queuebert.Client=a})();if(typeof(exports)!=="undefined"){exports.Client=Queuebert.Client}if(typeof(Queuebert)==="undefined"){Queuebert={}}if(!Queuebert.Client&&typeof(require)!=="undefined"&&typeof(exports)!=="undefined"){Queuebert.Client=require("./client").Client}(function(){function b(c){var d=c||{},e=this;this.delegate=d.delegate||{};this.identifier=d.identifier||"background";this.server=d.server;this.messageInterval=d.messageInterval||150;this.setInterval=d.setInterval||function(g,f){setInterval(g,f)};this.sendRequest=function(f,g){if(f.type=="get"){f.tabId="background"}e.server.handleMessage(f,{tab:{id:"background"}},g)};if(d.start===undefined||d.start){this.start()}}for(var a in Queuebert.Client.prototype){b.prototype[a]=Queuebert.Client.prototype[a]}Queuebert.BackgroundClient=b})();if(typeof(exports)!=="undefined"){exports.BackgroundClient=Queuebert.BackgroundClient}if(typeof(Queuebert)==="undefined"){Queuebert={}}(function(){function a(b){var d=this,c=b||{};this.messages={};this.maxQueueSize=c.maxQueueSize||128;this.onRequest=c.onRequest||chrome.extension.onRequest;this.onRequest.addListener(function(f,e,g){d.handleMessage(f,e,g)})}a.prototype.handleMessage=function(f,d,h){var b=h||function(){},c=f.tabId||this.getTabId(d);this.registerClient(f.to,c);this.registerClient(f.from,c);if(f.type=="put"){var e={from:f.from,action:f.action,body:f.body,tabId:this.getTabId(d)};if(h){e.callback=h}this.messages[c][f.to].push(e);if(this.messages[c][f.to].length>this.maxQueueSize){this.messages[c][f.to].shift()}}else{if(f.type=="get"){var g=this.messages[c][f.from].shift();if(g){b(g)}else{b(null)}}}};a.prototype.registerClient=function(c,b){if(typeof(this.messages[b])!=="object"){this.messages[b]={}}if(typeof(this.messages[b][c])==="object"){return}this.messages[b][c]=[]};a.prototype.getTabId=function(b){return"tab_"+b.tab.id};Queuebert.Server=a})();if(typeof(exports)!=="undefined"){exports.Server=Queuebert.Server}; -------------------------------------------------------------------------------- /example/background.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /example/content-script.js: -------------------------------------------------------------------------------- 1 | var delegate = { 2 | receivedMessage: function(clientId, clientTabId, body) { 3 | console.log(body.message); 4 | } 5 | }; 6 | 7 | var client = new Queuebert.Client({ 8 | identifier: 'client', 9 | delegate: delegate 10 | }); 11 | 12 | setInterval(function() { 13 | client.sendMessage({ 14 | action: 'echo', 15 | tabId: 'background', 16 | to: 'background', 17 | body: {message: 'Hello World!'} 18 | }); 19 | }, 2000); 20 | 21 | setInterval(function() { 22 | client.sendMessage({ 23 | action: 'add', 24 | tabId: 'background', 25 | to: 'background2', 26 | body: {x: 5, y: 20} 27 | }); 28 | }, 4000); 29 | -------------------------------------------------------------------------------- /images/queuebert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/attachmentsme/Queuebert/201ca77e5bca5563c941331145091971ff8f5d21/images/queuebert.png -------------------------------------------------------------------------------- /lib/background-client.js: -------------------------------------------------------------------------------- 1 | if (typeof(Queuebert) === 'undefined') { 2 | Queuebert = {}; 3 | } 4 | 5 | // Specifically for our unit tests which are written in Node.js. 6 | if (!Queuebert.Client && typeof(require) !== 'undefined' && typeof(exports) !== 'undefined') { 7 | Queuebert.Client = require('./client').Client; 8 | } 9 | 10 | (function() { 11 | function BackgroundClient(_params) { 12 | var params = _params || {}, 13 | _this = this; 14 | 15 | this.delegate = params.delegate || {}; 16 | this.identifier = params.identifier || 'background'; 17 | this.server = params.server; 18 | this.messageInterval = params.messageInterval || 150; 19 | this.setInterval = params.setInterval || function(callback, time) { setInterval(callback, time); }; 20 | 21 | this.sendRequest = function(request, callback) { 22 | if (request.type == 'get') { 23 | request.tabId = 'background'; 24 | } 25 | _this.server.handleMessage(request, {tab: {id: 'background'}}, callback); 26 | }; 27 | 28 | if (params.start === undefined || params.start) { 29 | this.start(); 30 | } 31 | }; 32 | 33 | for (var key in Queuebert.Client.prototype) { 34 | BackgroundClient.prototype[key] = Queuebert.Client.prototype[key]; 35 | } 36 | 37 | Queuebert.BackgroundClient = BackgroundClient; 38 | })(); 39 | 40 | // Specifically for our unit tests which are written in Node.js. 41 | if (typeof(exports) !== 'undefined') { 42 | exports.BackgroundClient = Queuebert.BackgroundClient; 43 | } 44 | -------------------------------------------------------------------------------- /lib/client.js: -------------------------------------------------------------------------------- 1 | if (typeof(Queuebert) === 'undefined') { 2 | Queuebert = {}; 3 | } 4 | 5 | (function() { 6 | function Client(_params) { 7 | var params = _params || {}; 8 | this.delegate = params.delegate || {}; 9 | this.identifier = params.identifier; 10 | this.messageInterval = params.messageInterval || 150; 11 | this.sendRequest = params.sendRequest || chrome.extension.sendRequest; 12 | this.setInterval = params.setInterval || function(callback, time) { setInterval(callback, time); }; 13 | 14 | if (params.start === undefined || params.start) { 15 | this.start(); 16 | } 17 | } 18 | 19 | Client.prototype.start = function() { 20 | var _this = this; 21 | 22 | this.setInterval(function() { 23 | _this.sendRequest({from: _this.identifier, type: 'get'}, function(rawMessage) { 24 | if (rawMessage) { 25 | _this.handleMessage(rawMessage.action, rawMessage.from, rawMessage.tabId, rawMessage.body, rawMessage.callback); 26 | } 27 | }); 28 | }, this.messageInterval); 29 | } 30 | 31 | Client.prototype.handleMessage = function(action, clientId, clientTabId, body, callback) { 32 | if (this.delegate[action]) { 33 | this.delegate[action](clientId, clientTabId, body, callback); 34 | } 35 | }; 36 | 37 | Client.prototype.sendMessage = function(msg, callback) { 38 | 39 | var message = { 40 | tabId: msg.tabId, 41 | to: msg.to, 42 | from: this.identifier, 43 | action: msg.action, 44 | type: 'put', 45 | body: msg.body 46 | }; 47 | 48 | callback ? this.sendRequest(message, callback) : this.sendRequest(message); 49 | }; 50 | 51 | Queuebert.Client = Client; 52 | })(); 53 | 54 | // Specifically for our unit tests which are written in Node.js. 55 | if (typeof(exports) !== 'undefined') { 56 | exports.Client = Queuebert.Client; 57 | } 58 | -------------------------------------------------------------------------------- /lib/server.js: -------------------------------------------------------------------------------- 1 | if (typeof(Queuebert) === 'undefined') { 2 | Queuebert = {}; 3 | } 4 | 5 | (function() { 6 | function Server(_params) { 7 | var _this = this, 8 | params = _params || {}; 9 | 10 | this.messages = {}; 11 | this.maxQueueSize = params.maxQueueSize || 128; 12 | this.onRequest = params.onRequest || chrome.extension.onRequest; 13 | this.onRequest.addListener(function(request, sender, callback) { 14 | _this.handleMessage(request, sender, callback); 15 | }); 16 | } 17 | 18 | Server.prototype.handleMessage = function(request, sender, callback) { 19 | var _callback = callback || function() {}, 20 | tabId = request.tabId || this.getTabId(sender); 21 | 22 | this.registerClient(request.to, tabId); 23 | this.registerClient(request.from, tabId); 24 | 25 | if (request.type == 'put') { 26 | var message = { 27 | from: request.from, 28 | action: request.action, 29 | body: request.body, 30 | tabId: this.getTabId(sender) 31 | } 32 | 33 | if (callback) { 34 | message['callback'] = callback; 35 | } 36 | 37 | this.messages[ tabId ][request.to].push(message); 38 | 39 | if (this.messages[ tabId ][request.to].length > this.maxQueueSize) { 40 | this.messages[ tabId ][request.to].shift(); 41 | } 42 | } else if(request.type == 'get') { 43 | var rawMessage = this.messages[tabId][request.from].shift(); 44 | if (rawMessage) { 45 | _callback(rawMessage); 46 | } else { 47 | _callback(null); 48 | } 49 | } 50 | }; 51 | 52 | Server.prototype.registerClient = function(identifier, tabId) { 53 | 54 | if (typeof(this.messages[tabId]) !== 'object') { 55 | this.messages[tabId] = {}; 56 | } 57 | 58 | if (typeof(this.messages[tabId][identifier]) === 'object') return; 59 | this.messages[tabId][identifier] = []; 60 | }; 61 | 62 | Server.prototype.getTabId = function(sender) { 63 | return 'tab_' + sender.tab.id; 64 | }; 65 | 66 | Queuebert.Server = Server; 67 | })(); 68 | 69 | // Specifically for our unit tests which are written in Node.js. 70 | if (typeof(exports) !== 'undefined') { 71 | exports.Server = Queuebert.Server; 72 | } 73 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Queuebert", 3 | "version": "0.0.1", 4 | "description": "A queue for communicating between iframes and background.html in your chrome extension.", 5 | "background_page" : "example/background.html", 6 | "page_action": {}, 7 | "icons": {}, 8 | "permissions": ["tabs"], 9 | "content_scripts": [ 10 | { 11 | "matches": ["http://*/", "https://*/"], 12 | "js": [ 13 | "lib/client.js", 14 | "example/content-script.js" 15 | ], 16 | "all_frames": false 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Queuebert", 3 | "directories": { 4 | "lib": "./lib" 5 | }, 6 | "version": "1.0.0", 7 | "author": "Ben Coe ", 8 | "engines": [ 9 | "node" 10 | ], 11 | "description": "A queue for communicating between iframes and background.html in your chrome extension.", 12 | "keywords": [ 13 | "chrome", 14 | "queuebert" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/attachmentsme/Queuebert.git" 19 | }, 20 | "dependencies": { 21 | "micro-test": "1.0.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/background-client-test.js: -------------------------------------------------------------------------------- 1 | var equal = require('assert').equal, 2 | puts = require('util').puts, 3 | BackgroundClient = require('../lib/background-client').BackgroundClient; 4 | 5 | exports.tests = { 6 | 'background client should have methods copied over from client': function(finished, prefix) { 7 | var backgroundClient = new BackgroundClient({start: false}); 8 | equal(typeof(backgroundClient.handleMessage), 'function', prefix + ' client not extended.'); 9 | finished(); 10 | }, 11 | 12 | 'calling send message with a background client should invoke handleMessage directly': function(finished, prefix) { 13 | var backgroundClient = new BackgroundClient({ 14 | server: { 15 | handleMessage: function(request, sender, callback) { 16 | equal(sender.tab.id, 'background', prefix + ' tabId was not correct'); 17 | equal(request.body.foo, 'bar', prefix + ' foo was not equal to bar'); 18 | finished(); 19 | } 20 | }, 21 | start: false 22 | }); 23 | backgroundClient.sendMessage({ 24 | action: 'upload', 25 | body: {foo: 'bar'} 26 | }); 27 | }, 28 | 29 | 'message interval set to default value and be able to be overridden.': function(finished, prefix) { 30 | var backgroundClient = new BackgroundClient({ 31 | identifier: 'client_identifier', 32 | start: false, 33 | sendRequest: function(params, callback) {} 34 | }); 35 | equal(backgroundClient.messageInterval, 150, prefix + ' default messageInterval not set'); 36 | 37 | backgroundClient = new BackgroundClient({ 38 | messageInterval: 50, 39 | identifier: 'client_identifier', 40 | start: false, 41 | sendRequest: function(params, callback) {} 42 | }); 43 | equal(backgroundClient.messageInterval, 50, prefix + ' default messageInterval not set'); 44 | 45 | finished(); 46 | } 47 | }; -------------------------------------------------------------------------------- /test/client-test.js: -------------------------------------------------------------------------------- 1 | var equal = require('assert').equal, 2 | puts = require('util').puts, 3 | Client = require('../lib/client').Client; 4 | 5 | exports.tests = { 6 | 'should override default sendRequest object with one passed in': function(finished, prefix) { 7 | var client = new Client({ 8 | identifier: 'client_identifier', 9 | start: false, 10 | sendRequest: function(params, callback) { 11 | equal(true, true); 12 | finished(); 13 | } 14 | }); 15 | client.sendRequest(null, null); 16 | }, 17 | 18 | 'send message should invoke sendRequest with the appropriate parameters': function(finished, prefix) { 19 | var client = new Client({ 20 | identifier: 'client_identifier', 21 | start: false, 22 | sendRequest: function(params, callback) { 23 | equal(params.tabId, 'tab_2', prefix + ' tabId was not set.'); 24 | equal(params.type, 'put', prefix + ' type was not put.'); 25 | equal(params.to, 'client_identifier', prefix + ' client identifier not set'); 26 | equal(params.from, 'client_identifier', prefix + ' from should be set'); 27 | equal(params.body.foo, 'bar', prefix + ' foo was not equal to bar'); 28 | equal(params.action, 'upload', prefix + ' upload action was not set'); 29 | finished(); 30 | } 31 | }); 32 | client.sendMessage( { 33 | tabId: 'tab_2', 34 | to: 'client_identifier', 35 | action: 'upload', 36 | body: { 37 | foo: 'bar' 38 | } 39 | }); 40 | }, 41 | 42 | 'client should receive messages on a set interval': function(finished, prefix) { 43 | var fin = false; 44 | 45 | var clientDelegate = { 46 | upload: function(senderId, senderTabId, body) { 47 | equal(senderTabId, 'tab_1', prefix + ' senderId not set.'); 48 | equal(senderId, 'client_identifier', prefix + ' senderId not set.'); 49 | equal(body.foo, 'bar', prefix + ' foo not equal to bar.'); 50 | if (!fin) { 51 | fin = true; 52 | finished(); 53 | } 54 | } 55 | } 56 | new Client({ 57 | delegate: clientDelegate, 58 | identifier: 'client_identifier', 59 | sendRequest: function(params, callback) { 60 | callback({ 61 | action: 'upload', 62 | from: 'client_identifier', 63 | tabId: 'tab_1', 64 | body: {foo: 'bar'} 65 | }); 66 | } 67 | }); 68 | }, 69 | 70 | 'message interval set to default value and be able to be overridden.': function(finished, prefix) { 71 | var client = new Client({ 72 | identifier: 'client_identifier', 73 | start: false, 74 | sendRequest: function(params, callback) {} 75 | }); 76 | equal(client.messageInterval, 150, prefix + ' default messageInterval not set'); 77 | 78 | client = new Client({ 79 | messageInterval: 50, 80 | identifier: 'client_identifier', 81 | start: false, 82 | sendRequest: function(params, callback) {} 83 | }); 84 | equal(client.messageInterval, 50, prefix + ' default messageInterval not set'); 85 | 86 | finished(); 87 | } 88 | } -------------------------------------------------------------------------------- /test/server-test.js: -------------------------------------------------------------------------------- 1 | var equal = require('assert').equal, 2 | puts = require('util').puts, 3 | Server = require('../lib/server').Server; 4 | 5 | exports.tests = { 6 | 'should override default onRequest object with one passed in': function(finished, prefix) { 7 | var server = new Server({ 8 | onRequest: { 9 | addListener: function(fn) { 10 | equal(true, true); 11 | finished(); 12 | } 13 | } 14 | }); 15 | }, 16 | 17 | 'the first time a client sends a message, we should create a message queue for that client': function(finished, prefix) { 18 | var server = new Server({ 19 | onRequest: { 20 | addListener: function(fn) { 21 | fn({to: 'client_identifier'}, {tab: { id: 1 } }); 22 | } 23 | } 24 | }); 25 | equal(typeof(server.messages['tab_1']['client_identifier']), 'object', prefix + ' no queue object existed for client'); 26 | finished(); 27 | }, 28 | 29 | 'two different client ids should not cause messages queue to reset': function(finished, prefix) { 30 | var server = new Server({ 31 | onRequest: { 32 | addListener: function(fn) { 33 | fn({to: 'to_identifier', from: 'from_identifier'}, {tab: { id: 1 } }); 34 | } 35 | } 36 | }); 37 | equal(typeof(server.messages['tab_1']['to_identifier']), 'object', prefix + ' messages queue was reset.'); 38 | finished(); 39 | }, 40 | 41 | 'should not reset client message queue each time a client provides a message': function(finished, prefix) { 42 | var server = new Server({ 43 | onRequest: { 44 | addListener: function(fn) { 45 | fn({to: 'client_identifier', type: 'put'}, {tab: { id: 1 } }); 46 | } 47 | } 48 | }); 49 | 50 | server.handleMessage({to: 'client_identifier', type: 'put'}, {tab: { id: 1 } }); 51 | equal(server.messages['tab_1']['client_identifier'].length, 2, prefix + ' messages should have two messages in it'); 52 | finished(); 53 | }, 54 | 55 | 'handleMessage should push a message on to a client queue': function(finished, prefix) { 56 | var server = new Server({ 57 | onRequest: { 58 | addListener: function(fn) {} 59 | } 60 | }); 61 | server.handleMessage({to: 'recipient_identifier', body: {foo: 'bar'}, type: 'put'}, {tab: {id: 1}}); 62 | equal(server.messages['tab_1']['recipient_identifier'][0].body.foo, 'bar', prefix + ' foo was not equal to bar.'); 63 | finished(); 64 | }, 65 | 66 | 'get message should grab a message for a client based on identifier and tab.id': function(finished, prefix) { 67 | var server = new Server({ 68 | onRequest: { 69 | addListener: function(fn) {} 70 | } 71 | }); 72 | server.handleMessage({to: 'recipient_identifier', body: {foo: 'bar'}, type: 'put', from: 'sender_identifier'}, {tab: {id: 1}}); 73 | server.handleMessage({from: 'recipient_identifier', type: 'get'}, {tab: {id: 1}}, function(rawMessage) { 74 | equal(rawMessage.tabId, 'tab_1', prefix + ' tab id was not passed back in raw message.'); 75 | equal(rawMessage.from, 'sender_identifier', prefix + ' recipient id not correct.'); 76 | equal(rawMessage.body.foo, 'bar', prefix + ' foo was not equal to bar.'); 77 | finished(); 78 | }); 79 | }, 80 | 81 | 'get message should not explode if there are zero messages in the queue': function(finished, prefix) { 82 | var server = new Server({ 83 | onRequest: { 84 | addListener: function(fn) {} 85 | } 86 | }); 87 | server.handleMessage({to: 'recipient_identifier', type: 'get'}, {tab: {id: 1}}, function(rawMessage) { 88 | equal(null, rawMessage, prefix + ' body should be null.'); 89 | finished(); 90 | }); 91 | finished(); 92 | }, 93 | 94 | 'you should be able to deliver a message to an arbitrary tabId': function(finished, prefix) { 95 | var server = new Server({ 96 | onRequest: { 97 | addListener: function(fn) {} 98 | } 99 | }); 100 | server.handleMessage({tabId: 'tab_2', to: 'recipient_identifier', body: {foo: 'bar'}, type: 'put', from: 'sender_identifier'}, {tab: {id: 1}}); 101 | server.handleMessage({from: 'recipient_identifier', type: 'get', tabId: 'tab_2'}, {tab: {id: 1}}, function(rawMessage) { 102 | equal(rawMessage.tabId, 'tab_1', prefix + ' tab id was not passed back in raw message.'); 103 | equal(rawMessage.from, 'sender_identifier', prefix + ' recipient id not correct.'); 104 | equal(rawMessage.body.foo, 'bar', prefix + ' foo was not equal to bar.'); 105 | finished(); 106 | }); 107 | }, 108 | 109 | 'the messages queue for each client should be capped at max queue size': function(finished, prefix) { 110 | var server = new Server({ 111 | maxQueueSize: 3, 112 | onRequest: { 113 | addListener: function(fn) { 114 | fn({to: 'client_identifier', type: 'put'}, {tab: { id: 1 } }); 115 | } 116 | } 117 | }); 118 | 119 | server.handleMessage({to: 'client_identifier', type: 'put', body: {value: 1}}, {tab: { id: 1 } }); 120 | server.handleMessage({to: 'client_identifier', type: 'put', body: {value: 2}}, {tab: { id: 1 } }); 121 | server.handleMessage({to: 'client_identifier', type: 'put', body: {value: 3}}, {tab: { id: 1 } }); 122 | server.handleMessage({to: 'client_identifier', type: 'put', body: {value: 4}}, {tab: { id: 1 } }); 123 | 124 | equal(server.messages['tab_1']['client_identifier'].length, 3, prefix + ' messages should be capped at three messages'); 125 | equal(server.messages['tab_1']['client_identifier'][0].body.value, 2, prefix + ' wrong value in queue'); 126 | equal(server.messages['tab_1']['client_identifier'][2].body.value, 4, prefix + ' wrong value in queue'); 127 | finished(); 128 | } 129 | }; --------------------------------------------------------------------------------