Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,231 @@
/* global Marionette */
(function(module, ns) {
'use strict';
var Command = require('../message').Command;
var Response = require('../message').Response;
/**
*
* Abstract driver that will handle
* all common tasks between implementations.
* Such as error handling, request/response queuing
* and timeouts.
*
* @constructor
* @class Marionette.Drivers.Abstract
* @param {Object} options set options on prototype.
*/
function Abstract(options) {
this._sendQueue = [];
this._responseQueue = [];
this._lastId = 0;
}
Abstract.prototype = {
/**
* Duration in milliseconds to wait before timing out a command
* request.
*
* @property timeout
* @type Number
*/
timeout: 10000,
/**
* Waiting for a command to finish?
*
* @private
* @property _waiting
* @type Boolean
*/
_waiting: true,
/**
* Is system ready for commands?
*
* @property ready
* @type Boolean
*/
ready: false,
/**
* A unique identifier for the current connection.
*
* @property connectionId
* @type Number
*/
connectionId: null,
/**
* Duration in milliseconds defining when to abort an attempt to
* execute script in the remote end.
*
* @method setScriptTiemout
* @param {Number} the timeout value.
*/
setScriptTimeout: function setScriptTimeout(timeout) {
},
/**
* Sends a JSON data structure across the wire to the remote end.
*
* Each command is queued whilst waiting for any pending commands.
* This ensures order of response is correct, but also means the client
* implements a synchronous interface for Marionette.
*
* @method send
*
* @param {(Object|Command|Response)} obj
* An object that can be dumped into a JSON data structure,
* or a message that can be marshaled.
* @param {Function} callback
* Callback to be invoked when a response for the request is
* received.
*/
send: function send(obj, callback) {
if (!this.ready) {
throw new Error('Connection is not ready');
}
if (typeof callback == 'undefined') {
throw new Error('Callback is required');
}
if (obj instanceof Command || obj instanceof Response) {
this.sendMessage(obj, callback);
} else {
this.sendRaw(obj, callback);
}
return this;
},
/**
* Sends a message across the wire to the remote end.
*
* @param {(Command|Response)} message
* The message to send.
* @param {Function} callback
* Callback to be invoked when a response for the request is
* received.
*/
sendMessage: function(message, callback) {
message.id = ++this._lastId;
this.sendRaw(message.toMsg(), callback);
},
/**
* Sends a JSON data structure across the wire to the remote end.
*
* @param {Object} packet
* An object that can be marshaled into a JSON data structure.
* @param {Function} callback
* Callback to be invoked when a response for the request is
* received.
*/
sendRaw: function(packet, callback) {
this._responseQueue.push(callback);
this._sendQueue.push(packet);
this._nextCommand();
},
/**
* Connects to the remote end.
*
* Requires the _connect function to be defined in the concrete class.
*
* MyConcreteClass.prototype._connect = function _connect(){
* // open a socket to marrionete accept response
* // you *must* call _onDeviceResponse with the first
* // response from Marionette it looks like this:
* // {applicationType: "gecko", marionetteProtocol: 2}
* this.connectionId = result.id;
* }
*
* @method connect
* @param {Function} callback executes
* after successfully connecting to the server.
*/
connect: function connect(callback) {
this.ready = true;
this._responseQueue.push(function(data) {
this.marionetteProtocol = data.marionetteProtocol || 1;
this.traits = data.traits;
this.applicationType = data.applicationType;
callback();
}.bind(this));
this._connect();
},
/**
* Destroys connection to server
*
* Will immediately close connection to server
* closing any pending responses.
*
* @method close
*/
close: function() {
this.ready = false;
this._responseQueue.length = 0;
if (this._close) {
this._close();
}
},
/**
* Checks queue if not waiting for a response
* Sends command to websocket server
*
* @private
* @method _nextCommand
*/
_nextCommand: function _nextCommand() {
if (!this._waiting && this._sendQueue.length) {
this._waiting = true;
var next = this._sendQueue.shift();
this._sendCommand(next);
}
},
/**
* Handles responses from devices.
* Will only respond to the event if the connectionId
* is equal to the event id and the client is ready.
*
* @param {Object} data response from server.
* @private
* @method _onDeviceResponse
*/
_onDeviceResponse: function _onDeviceResponse(data) {
if (this.ready && data.id === this.connectionId) {
this._waiting = false;
var resp;
if (this.marionetteProtocol >= 3) {
var msg = Response.fromMsg(data.response);
resp = msg.result;
} else {
resp = data.response;
}
var cb = this._responseQueue.shift();
cb(resp);
this._nextCommand();
}
}
};
module.exports = Abstract;
}.apply(
this,
(this.Marionette) ?
[Marionette('drivers/abstract'), Marionette] :
[module, require('../marionette')]
));

View file

@ -0,0 +1,23 @@
/* global Marionette */
(function(module, ns) {
'use strict';
module.exports = {
Abstract: ns.require('drivers/abstract')
};
if (typeof(window) === 'undefined') {
module.exports.Tcp = require('./tcp');
module.exports.TcpSync = require('./tcp-sync');
} else {
if (typeof(window.TCPSocket) !== 'undefined') {
module.exports.MozTcp = ns.require('drivers/moz-tcp');
}
}
}.apply(
this,
(this.Marionette) ?
[Marionette('drivers'), Marionette] :
[module, require('../marionette')]
));

View file

@ -0,0 +1,233 @@
'use strict';
var debug = require('debug')('marionette:tcp-sync');
var wire = require('json-wire-protocol');
var sockittome = require('sockit-to-me');
var Command = require('../message').Command;
var Response = require('../message').Response;
var DEFAULT_HOST = 'localhost';
var DEFAULT_PORT = 2828;
var SOCKET_TIMEOUT_EXTRA = 500;
function TcpSync(options) {
if (!options) {
options = {};
}
this.host = options.host || DEFAULT_HOST;
this.port = options.port || DEFAULT_PORT;
this.connectionTimeout = options.connectionTimeout || 2000;
this.isSync = true;
this.retryInterval = 300;
this.sockit = new sockittome.Sockit();
this.sockit.setPollTimeout(this.connectionTimeout + SOCKET_TIMEOUT_EXTRA);
this._lastId = 0;
}
TcpSync.prototype._createSocketConfig = function() {
return {host: this.host, port: this.port};
};
TcpSync.prototype.setScriptTimeout = function(timeout) {
this.sockit.setPollTimeout(timeout + SOCKET_TIMEOUT_EXTRA);
};
/**
* Utility to wait for the marionette socket to be ready.
*
* @method waitForSocket
* @param {Object} [options] for timeout.
* @param {Number} [options.interval] time between running test.
* @param {Number} [options.timeout] maximum wallclock time before
* failing test.
* @param {Function} [callback] callback.
*/
TcpSync.prototype.waitForSocket = function(options, callback) {
// the logic is lifted from
// mozilla-central/source/testing/marionette/client/marionette/marionette.py
if (typeof(options) === 'function') {
callback = options;
options = null;
}
options = options || {};
var interval = options.interval || 100;
var timeout = options.timeout || 300000;
var socketTimeout = 1000;
var sockit = new sockittome.Sockit();
var start = Date.now();
var lastDebugMessage = '';
var self = this;
// Use sockittome's built in polling timeout during calls to connect
// to avoid socket misuse.
sockit.setPollTimeout(socketTimeout);
var probeSocket = function() {
try {
sockit.connect(this._createSocketConfig());
var s = sockit.read(16).toString();
sockit.close();
if (s.indexOf(':') != -1) {
callback();
return;
}
} catch (e) {
// This may seem ridiculous, but we have to keep these errors showing up
// but, they often repeat like CRAZY, so, quiet them down by only showing
// each exception we encounter once.
if (lastDebugMessage != e.message) {
lastDebugMessage = e.message;
debug('exception when probing socket', lastDebugMessage);
}
// Above read _may_ fail so it is important to close the socket...
sockit.close();
}
// timeout. Abort.
if ((Date.now() - start) > timeout) {
console.error('Timeout connecting to B2G');
return;
}
// interval delay for the next iteration.
setTimeout(probeSocket.bind(self), interval);
}.bind(this);
debug('probing socket');
probeSocket();
};
TcpSync.prototype.connect = function(callback) {
this.waitForSocket(function _connect() {
try {
this.sockit.connect(this._createSocketConfig());
} catch(err) {
if (Date.now() - this._beginConnect >= this.connectionTimeout) {
callback(err);
}
setTimeout(_connect.bind(this, callback), this.retryInterval);
return;
}
if (!this._beginConnect) {
this._beginConnect = Date.now();
}
// Ensure this method's resolution is asynchronous in all cases
process.nextTick(function() {
debug('socket connected');
delete this._beginConnect;
var resp = this._readResponse();
this.marionetteProtocol = resp.marionetteProtocol || 1;
this.traits = resp.traits;
this.applicationType = resp.applicationType;
callback();
}.bind(this));
}.bind(this));
};
TcpSync.prototype.defaultCallback = function(err, result) {
if (err) {
throw err;
}
return result;
};
// Following the json-wire-protocol implementation, read one byte at a time
// until the 'separator' character is received. The data preceding the
// delimiter describes the length of the JSON string.
TcpSync.prototype._readResponse = function() {
var stream = new wire.Stream();
var data, error;
stream.on('data', function(parsed) {
data = parsed;
});
stream.on('error', function(err) {
error = err;
});
while (!data && !error) {
stream.write(this.sockit.read(1));
}
if (error) {
throw error;
}
if (this.marionetteProtocol >= 3) {
var resp = Response.fromMsg(data);
return resp.error || resp.result;
}
return data;
};
/**
* Sends a JSON data structure across the wire to the remote end.
*
* @param {(Object|Command|Response)} obj
* An object that can be dumped into a JSON data structure,
* or a message that can be marshaled.
* @param {Function} cb
* Callback to be invoked when a response for the request is
* received.
*
* @return {?}
* The return value from the passed in callback.
*/
TcpSync.prototype.send = function(obj, cb) {
if (obj instanceof Command || obj instanceof Response) {
return this.sendMessage(obj, cb);
} else {
return this.sendRaw(obj, cb);
}
};
/**
* Sends a message across the wire to the remote end.
*
* @param {(Command|Response)} msg
* The message to send.
* @param {Function} cb
* Callback to be invoked when a response for the request is received.
*
* @return {?}
* The return value from the passed in callback.
*/
TcpSync.prototype.sendMessage = function(msg, cb) {
msg.id = ++this._lastId;
return this.sendRaw(msg.toMsg(), cb);
};
/**
* Sends a JSON data structure across the wire to the remote end.
*
* @param {Object} data
* An object that can be marshaled into a JSON data structure.
* @param {Function} cb
* Callback to be invoked when a response for the request is received.
*
* @return {?}
* The return value from the passed in callback.
*/
TcpSync.prototype.sendRaw = function(data, cb) {
debug('write', data);
this.sockit.write(wire.stringify(data));
return cb(this._readResponse());
};
TcpSync.prototype.close = function() {
this.sockit.close();
};
module.exports = TcpSync;

View file

@ -0,0 +1,106 @@
'use strict';
var Abstract = require('./abstract');
var CommandStream = require('../../marionette/command-stream');
var debug = require('debug')('marionette:tcp');
var net = require('net');
var retrySocket = require('socket-retry-connect');
/** TCP **/
Tcp.Socket = net.Socket;
/**
* NodeJS only tcp socket driver for marionette.
* See {{#crossLink "Marionette.Drivers.MozTcp"}}{{/crossLink}}
* for the gecko/xpcom vesion of this driver.
*
* @class Marionette.Drivers.Tcp
* @extends Marionette.Drivers.Abstract
* @constructor
* @param {Options} options connection options.
* @param {String} [options.host="localhost"] host.
* @param {Numeric} [options.port="2828"] port.
*/
function Tcp(options) {
if (!options) {
options = {};
}
Abstract.call(this, options);
/**
* The unique identifier of the current connection.
*
* @property connectionId
* @type Number
*/
this.connectionId = 0;
/**
* The remote hostname.
*
* @property host
* @type String
*/
this.host = options.host || 'localhost';
/**
* The remote port number.
*
* @property port
* @type Number
*/
this.port = options.port || 2828;
}
Tcp.prototype = Object.create(Abstract.prototype);
/**
* Sends a command to the server.
*
* @param {Object} cmd remote marionette command.
*/
Tcp.prototype._sendCommand = function _sendCommand(cmd) {
this.client.send(cmd);
};
/**
* Opens TCP socket for marionette client.
*/
Tcp.prototype._connect = function connect() {
var options = {
port: this.port,
host: this.host,
tries: this.tries
};
retrySocket.waitForSocket(options, function(err, socket) {
debug('got socket starting command stream');
this.socket = socket;
this.client = new CommandStream(this.socket);
this.client.on('command', this._onClientCommand.bind(this));
}.bind(this));
};
/**
* Receives command from server.
*
* @param {Object} data response from marionette server.
*/
Tcp.prototype._onClientCommand = function(data) {
this._onDeviceResponse({
id: this.connectionId,
response: data
});
};
/**
* Closes connection to marionette.
*/
Tcp.prototype._close = function close() {
if (this.socket && this.socket.destroy) {
this.socket.destroy();
}
};
/** export */
module.exports = exports = Tcp;