antimony

drive firefox from node.js
git clone https://wehaveforgeathome.hates.computer/antimony.git
Log | Files | Refs | Submodules | LICENSE

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:
Maddon/chrome/content/browser.xul | 2+-
Aaddon/chrome/content/content.js | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpublic/javascripts/runner.js | 17+++++++++++++++--
Arouter.js | 45+++++++++++++++++++++++++++++++++++++++++++++
Mserver.js | 86++++++++++++++++++++++++++++++++++++++-----------------------------------------
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);