Skip to content

Instantly share code, notes, and snippets.

@andreastt
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andreastt/b3f7f9c5fc3f01163da1 to your computer and use it in GitHub Desktop.
Save andreastt/b3f7f9c5fc3f01163da1 to your computer and use it in GitHub Desktop.
// Represents what we send back to the client.
// This is handed down to each individual command
// which is allowed to manipulate it using the getters and setters.
var Response = function(respHandler, opt_msg, opt_sanitizer) {
let sanitizer = function(map) {
let rv = {};
for (var [key, value] of map) {
if ((typeof value !== undefined) || value !== null)
rv[key] = value;
}
return rv;
};
this.respHandler = respHandler;
this.sanitizer = opt_sanitizer || sanitizer;
let msg = opt_msg || {};
this.data = {
name: msg ? msg.name : "Unknown command",
sessionId: "sessionId" in msg ? msg["sessionId"] : null,
status: "success",
value: null
};
};
Response.prototype = {
send: function() {
var rawData = this.sanitizer(this.data);
this.respHandler(rawData);
},
sendError: function(exc) {
this.status = (e instanceof WebDriverError) ? exc.code : "unknown error";
this.value = errors.toJSON(exc);
this.send();
},
set name(n) { this.data.name = name },
get name() { return this.data.name },
get sessionId() { return this.data.sessionId },
set sessionId(id) { this.data.sessionId = sessionId },
set status(ns) { this.data.status = ns },
get status() { return this.data.status },
set value(val) { this.data.value = val },
get value() { return this.data.val }
};
// CommandProcessor receives messages on execute(…) from Dispatcher,
// processes them, and passes them along to MarionetteServer.
var CommandProcessor = function(marionetteServer) {
this.driver = marionetteServer;
};
CommandProcessor.prototype.unmarshal = function(payload) {
try {
return JSON.parse(payload);
} catch (e) {
throw new UnknownError("Error parsing payload: " + payload);
}
};
CommandProcessor.prototype.execute = function(payload, respHandler) {
let msg, resp;
try {
msg = this.unmarshal(payload);
resp = new Response(respHandler, msg);
this.driver[msg.name](msg.parameters, resp);
} catch (e) {
response.sendError(e);
}
};
// Responsible for handling content side commands.
function MarionetteListener() {
this.startListeners();
}
MarionetteListener.prototype.startListeners = function() {
addMessageListenerId("Marionette:getCurrentUrl", this.dispatch(this.getCurrentUrl));
};
MarionetteListener.prototype.dispatch = function(func) {
return function(msg) {
// msg is an IPC construct from the message manager
// that contains the data passed from MarionetteServer:
// {commandId: "abc", data: {"value": "foo"}}
let params = msg.data.value;
func(params, resp);
// Possibly we have to marshal resp here:
sendAsyncMessage("Marionette:done", resp, msg.commandId);
};
};
MarionetteListener.prototype.getCurrentUrl = function(cmd, resp) {
resp.value = curFrame.location.href;
};
var commandSeq = 0; // global
// Transparenly proxies any method you call on it to the listener.
var ListenerProxy = function() {
__noSuchMethod__: function (name, args) {
let msg = args;
msg.commandId = commandSeq++;
this.messageManager.sendAsyncMessage("Marionette:" + name, msg);
}
};
// Responsible for handling chrome side commands,
// like today's MarionetteServerConnection,
// and forwarding content side things to the listener.
function MarionetteServer() {
this.listener = new ListenerProxy();
}
MarionetteServer.prototype.getCurrentUrl = function(cmd, resp) {
switch (this.context) {
case Context.CHROME:
resp.value = this.getCurrentWindow().location.href;
break;
case Context.B2G:
resp.value = this.listener.getCurrentUrl();
break;
case Context.CONTENT:
resp.value = this.curBrowser.tab.linkedContent.contentWindow.location.href;
break;
}
};
// Makes out what is currently MarionetteServerConnection.receiveMessage et.al.
function Dispatcher() {
this.conn = …
this.driver = new MarionetteServer();
this.commandProcessor = new CommandProcessor(this.driver);
}
// When we receive data on the socket from the client.
// Data format is Marionette protocol,
// that is a string with a slightly modified JSON structure.
Dispatcher.prototype.onPacket: function(packet) {
// TOOD: unmarshal transport packet
this.commandProcessor.execute(rawData, this.send);
};
// Triggered on message from listener.
// This data comes as a stringified JSON object.
Dispatcher.prototype.receiveMessage = function(msg) {
switch (msg.name) {
case "Marionette:done":
this.send(msg);
break;
}
};
// Callback from commands as well as messages from listener
// This message is marshaled and sent back to the client.
Dispatcher.prototype.send = function(message) {
let payload = JSON.stringify(message);
this.conn.send(payload);
};
// Data received on the socket _or_ from listener
var data = "{name: 'getCurrentUrl', sessionId: '666'}";
// Fake some data received on socket or via IPC from listener
var dispatcher = new Dispatcher();
dispatcher.receiveMessage(data);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment