commit 9d6301162f2801de460d624c75e0bb1e368a623c
parent 6c4401a33233ed1be2dc5012dd7ca6f43e58e76d
Author: Ryan Wolf <rwolf@borderstylo.com>
Date: Mon, 13 Dec 2010 18:17:39 -0800
first working demo of full end to end async test
Diffstat:
5 files changed, 154 insertions(+), 48 deletions(-)
diff --git a/addon/chrome/content/browser.xul b/addon/chrome/content/browser.xul
@@ -3,7 +3,7 @@
href="chrome://antimony/skin/stylesheets/stylesheet.css" ?>
<!DOCTYPE overlay SYSTEM "chrome://antimony/locale/stringbundle.dtd">
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
- <iframe name="antimony" style="display: none" src="http://localhost:1234/runner.html"></iframe>
+ <script type="text/javascript" src="chrome://antimony/content/content.js" />
<menubar id="main-menubar">
<menu label="&antimony.menu.title;" insertbefore="tools-menu">
<menupopup>
diff --git a/addon/chrome/content/content.js b/addon/chrome/content/content.js
@@ -0,0 +1,52 @@
+var Poller = function (url, handler) {
+ this.xhr = null;
+ this.url = url;
+ this.handler = handler;
+ this.connect();
+};
+
+Poller.prototype.connect = function () {
+ var that = this,
+ handler = this.handler,
+ xhr = this.xhr = new XMLHttpRequest();
+ xhr.open("GET", this.url, true);
+ xhr.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+ xhr.onreadystatechange = function (event) {
+ if (xhr.readyState >= 1 && xhr.readyState <= 3) { return; }
+ if (xhr.readyState === 0) { return that.restart(); }
+ if (xhr.status !== 200) { return that.restart(); }
+ var json = xhr.responseText;
+ that.restart();
+ handler(json);
+ };
+ xhr.setRequestHeader("Accept", "application/json");
+ xhr.send(null);
+};
+
+Poller.prototype.restart = function () {
+ this.xhr.abort();
+ this.connect();
+};
+
+var messageQueue = [];
+
+var running = false;
+var processNext = function () {
+ if (running || messageQueue.length === 0) { return; }
+ running = true;
+ var json = messageQueue.shift();
+ var message = JSON.parse(json);
+ eval('var f = ' + message.script);
+ f(function (data) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("POST", 'http://localhost:1235/browser/' + message.id, true);
+ xhr.send(JSON.stringify({res: data}));
+ running = false;
+ processNext();
+ });
+};
+
+new Poller('http://localhost:1234/', function (json) {
+ messageQueue.push(json);
+ processNext();
+});
diff --git a/public/javascripts/runner.js b/public/javascripts/runner.js
@@ -1,10 +1,11 @@
-netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead UniversalBrowserWrite UniversalXPConnect');
// handle communication with the server
var Server = function (requestHandler) {
var that = this;
this.requestHandler = requestHandler;
var socket = this.socket = new io.Socket('localhost', {
+ port: 1234,
transports: ['websocket', 'htmlfile', 'xhr-multipart', 'xhr-polling']
});
socket.connect();
@@ -19,6 +20,18 @@ Server.prototype.readMessage = function (json) {
this.requestHandler(obj);
};
+var Cc = Components.classes,
+ Ci = Components.interfaces;
+
+var Tab = function (url) {
+ var windowManager = Cc["@mozilla.org/appshell/window-mediator;1"].
+ getService(Ci.nsIWindowMediator);
+ var chromeWindow = windowManager.getMostRecentWindow("navigator:browser");
+ var gBrowser = chromeWindow.gBrowser;
+ var tab = gBrowser.addTab(url);
+};
+
var server = new Server(function (request) {
- alert(JSON.stringify(request));
+ new Tab(request.url);
});
+
diff --git a/router.js b/router.js
@@ -0,0 +1,45 @@
+var http = require('http');
+var url_parse = require('url').parse;
+
+var routes = {};
+
+var methods = ['get', 'put', 'post', 'delete'];
+
+methods.forEach(function (method) { routes[method] = []; });
+
+var addRoute = function (method, pattern, callback) {
+ var regex = new RegExp(
+ '^\/' +
+ pattern.replace(/\//g, '\\\/').replace(/\:\w+/g, '([^\/]+)') +
+ '$'
+ );
+ // TODO: generate regex from string pattern like Davis
+ routes[method].push({ regex: regex, callback: callback });
+};
+
+methods.forEach(function (method) {
+ exports[method] = function (pattern, callback) {
+ addRoute(method, pattern, callback);
+ };
+});
+
+exports.notFound = function (response) {
+ response.writeHead(404);
+ response.end('Not Found\n');
+};
+
+exports.server = http.createServer(function (request, response) {
+ var method = request.method.toLowerCase();
+ var path = url_parse(request.url).pathname.replace(/\/$/, '');
+ console.log(method + ': ' + path);
+ var matchingRoutes = routes[method].filter(function (route) {
+ return route.regex.test(path);
+ });
+ // TODO: handle no matches 404
+ if (matchingRoutes.length == 0) { return exports.notFound(response); }
+ // TODO: handle multiple matches 5**
+ var route = matchingRoutes[0];
+ var match = path.match(route.regex);
+ match.shift();
+ route.callback.apply(route.callback, [request, response].concat(match));
+});
diff --git a/server.js b/server.js
@@ -1,49 +1,45 @@
var http = require('http'),
- path = require('path'),
- fs = require('fs'),
- io = require('socket.io');
-
-var notFound = function (response) {
- console.log('not found');
- response.writeHead(404);
- response.end('Not Found');
-};
-
-var methodNotAllowed = function (allow, response) {
- console.log('method not allowed');
- response.writeHead(405, { Allow: allow.join(', ') });
- response.end('Method Not Allowed');
-};
-
-var serveStatic = function (filepath, response) {
- console.log('serving static ' + filepath);
- var extname = path.extname(filepath);
- var contentType = (extname === '.html') ? 'text/html' : 'text/javascript';
- response.writeHead(200, { 'Content-Type': contentType });
- var stream = fs.createReadStream(filepath);
- stream.on('data', function (data) { response.write(data); });
- stream.on('end', function (data) { response.end(data); });
-};
-
-var server = http.createServer(function (request, response) {
- console.log('GET: ' + request.url);
- var filepath = 'public' + request.url;
- path.exists(filepath, function (exists) {
- if (!exists) { return notFound(response); }
- if (request.method != 'GET') { methodNotAllowed(['GET'], response); }
- serveStatic(filepath, response);
+ router = require('./router');
+
+var pushResponses = [];
+
+var push = http.createServer(function (req, res) {
+ console.log('test');
+ pushResponses.push(res);
+});
+push.listen(1234);
+
+var clientResponses = {};
+
+router.post('client', function (req, res) {
+ var script = '';
+ req.on('data', function (data) { script += data; });
+ req.on('end', function (data) {
+ if (data) { script += data; }
+
+ var id = (new Date()).getTime();
+ clientResponses[id] = res;
+
+ var message = JSON.stringify({script : script, id: id});
+ pushRes = pushResponses.pop();
+ pushResponses = [];
+ pushRes.writeHead(200, {'Content-Type': 'application/json'});
+ pushRes.end(message);
});
});
-server.listen(1234);
-
-var socket = io.listen(server);
-socket.on('connection', function (client) {
- client.send('{"test":"success"}');
- // new client is here!
- client.on('message', function (message) {
- console.log(message);
- client.send('response from server');
- })
- client.on('disconnect', function () {
- })
+
+router.post('browser/:id', function (req, res, id) {
+ var json = '';
+ req.on('data', function (data) { json += data; });
+ req.on('end', function (data) {
+ if (data) { json += data; }
+ var clientRes = clientResponses[id];
+ delete clientResponses[id];
+ clientRes.writeHead(200);
+ clientRes.end(json);
+ res.writeHead(200);
+ res.end('fries are done');
+ });
});
+
+router.server.listen(1235);