Skip to content

Commit

Permalink
Merge from GCLI web
Browse files Browse the repository at this point in the history
This is an import of all the changes made in the GCLI repo. See here:
  https://github.com/joewalker/gcli/commits/master
The changes are those from 4 Feb 2015 (d59796b) to 12 Feb 2015 (fd63021)
  • Loading branch information
joewalker committed Feb 16, 2015
1 parent 50306fa commit 517c9a9
Show file tree
Hide file tree
Showing 19 changed files with 457 additions and 300 deletions.
9 changes: 5 additions & 4 deletions toolkit/devtools/gcli/source/lib/gcli/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ var evalCmd = {
var reply = customEval(args.javascript);
return context.typedData(typeof reply, reply);
},
isCommandRegexp: /^\s*{\s*/
isCommandRegexp: /^\s*\{\s*/
};

exports.items = [ evalCmd ];
Expand Down Expand Up @@ -694,7 +694,8 @@ Object.defineProperty(Requisition.prototype, 'status', {
*/
Requisition.prototype.getStatusMessage = function() {
if (this.commandAssignment.getStatus() !== Status.VALID) {
return l10n.lookup('cliUnknownCommand');
return l10n.lookupFormat('cliUnknownCommand2',
[ this.commandAssignment.arg.text ]);
}

var assignments = this.getAssignments();
Expand Down Expand Up @@ -1776,8 +1777,8 @@ Requisition.prototype._split = function(args) {
if (args[0].type === 'ScriptArgument') {
// Special case: if the user enters { console.log('foo'); } then we need to
// use the hidden 'eval' command
var evalCmd = this.system.commands.get(evalCmd.name)
conversion = new Conversion(evalCmd, new ScriptArgument());
var command = this.system.commands.get(evalCmd.name);
conversion = new Conversion(command, new ScriptArgument());
this._setAssignmentInternal(this.commandAssignment, conversion);
return;
}
Expand Down
15 changes: 13 additions & 2 deletions toolkit/devtools/gcli/source/lib/gcli/commands/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,9 +327,14 @@ exports.Parameter = Parameter;

/**
* A store for a list of commands
* @param types Each command uses a set of Types to parse its parameters so the
* Commands container needs access to the list of available types.
* @param location String that, if set will force all commands to have a
* matching runAt property to be accepted
*/
function Commands(types) {
function Commands(types, location) {
this.types = types;
this.location = location;

// A lookup hash of our registered commands
this._commands = {};
Expand All @@ -345,9 +350,15 @@ function Commands(types) {
/**
* Add a command to the list of known commands.
* @param commandSpec The command and its metadata.
* @return The new command
* @return The new command, or null if a location property has been set and the
* commandSpec doesn't have a matching runAt property.
*/
Commands.prototype.add = function(commandSpec) {
if (this.location != null && commandSpec.runAt != null &&
commandSpec.runAt !== this.location) {
return;
}

if (this._commands[commandSpec.name] != null) {
// Roughly commands.remove() without the event call, which we do later
delete this._commands[commandSpec.name];
Expand Down
40 changes: 18 additions & 22 deletions toolkit/devtools/gcli/source/lib/gcli/commands/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@

var l10n = require('../util/l10n');
var cli = require('../cli');
var GcliFront = require('../connectors/remoted').GcliFront;

/**
* A lookup of the current connection
*/
var connections = {};
var fronts = {};

/**
* 'connection' type
Expand All @@ -32,8 +33,8 @@ var connection = {
name: 'connection',
parent: 'selection',
lookup: function() {
return Object.keys(connections).map(function(prefix) {
return { name: prefix, value: connections[prefix] };
return Object.keys(fronts).map(function(prefix) {
return { name: prefix, value: fronts[prefix] };
});
}
};
Expand Down Expand Up @@ -87,19 +88,19 @@ var connect = {
returnType: 'string',

exec: function(args, context) {
if (connections[args.prefix] != null) {
if (fronts[args.prefix] != null) {
throw new Error(l10n.lookupFormat('connectDupReply', [ args.prefix ]));
}

var connector = args.method || context.system.connectors.get('xhr');
args.method = args.method || context.system.connectors.get('xhr');

return connector.connect(args.url).then(function(connection) {
// Nasty: stash the prefix on the connection to help us tidy up
connection.prefix = args.prefix;
connections[args.prefix] = connection;
return GcliFront.create(args.method, args.url).then(function(front) {
// Nasty: stash the prefix on the front to help us tidy up
front.prefix = args.prefix;
fronts[args.prefix] = front;

return connection.call('specs').then(function(specs) {
var remoter = this.createRemoter(args.prefix, connection);
return front.specs().then(function(specs) {
var remoter = this.createRemoter(args.prefix, front);
var commands = cli.getMapping(context).requisition.system.commands;
commands.addProxyCommands(specs, remoter, args.prefix, args.url);

Expand All @@ -116,7 +117,7 @@ var connect = {
* When we register a set of remote commands, we need to provide a proxy
* executor. This is that executor.
*/
createRemoter: function(prefix, connection) {
createRemoter: function(prefix, front) {
return function(cmdArgs, context) {
var typed = context.typed;

Expand All @@ -126,12 +127,7 @@ var connect = {
typed = typed.substring(prefix.length).replace(/^ */, '');
}

var data = {
typed: typed,
args: cmdArgs
};

return connection.call('execute', data).then(function(reply) {
return front.execute(typed).then(function(reply) {
var typedData = context.typedData(reply.type, reply.data);
if (!reply.error) {
return typedData;
Expand Down Expand Up @@ -162,11 +158,11 @@ var disconnect = {
returnType: 'string',

exec: function(args, context) {
var connection = args.prefix;
return connection.disconnect().then(function() {
var front = args.prefix;
return front.connection.disconnect().then(function() {
var commands = cli.getMapping(context).requisition.system.commands;
var removed = commands.removeProxyCommands(connection.prefix);
delete connections[connection.prefix];
var removed = commands.removeProxyCommands(front.prefix);
delete fronts[front.prefix];
return l10n.lookupFormat('disconnectReply', [ removed.length ]);
});
}
Expand Down
13 changes: 10 additions & 3 deletions toolkit/devtools/gcli/source/lib/gcli/connectors/connectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,21 @@ Connectors.prototype.getAll = function() {
}.bind(this));
};

var defaultConnectorName;

/**
* Get access to a connector by name. If name is undefined then use the first
* registered connector as a default.
* Get access to a connector by name. If name is undefined then first try to
* use the same connector that we used last time, and if there was no last
* time, then just use the first registered connector as a default.
*/
Connectors.prototype.get = function(name) {
if (name == null) {
name = Object.keys(this._registered)[0];
name = (defaultConnectorName == null) ?
Object.keys(this._registered)[0] :
defaultConnectorName;
}

defaultConnectorName = name;
return this._registered[name];
};

Expand Down
184 changes: 33 additions & 151 deletions toolkit/devtools/gcli/source/lib/gcli/connectors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,170 +16,52 @@

'use strict';

var api = require('../api');
var Commands = require('../commands/commands').Commands;
var Types = require('../types/types').Types;
var createSystem = require('../system').createSystem;
var connectSystems = require('../system').connectSystems;

// Patch-up IE9
require('../util/legacy');

/*
* GCLI is built from a number of components (called items) composed as
* required for each environment.
* When adding to or removing from this list, we should keep the basics in sync
* with the other environments.
* See:
* - lib/gcli/index.js: Generic basic set (without commands)
* - lib/gcli/demo.js: Adds demo commands to basic set for use in web demo
* - gcli.js: Add commands to basic set for use in Node command line
* - lib/gcli/index.js: (mozmaster branch) From scratch listing for Firefox
* - lib/gcli/connectors/index.js: Client only items when executing remotely
* - lib/gcli/connectors/direct.js: Test items for connecting to in-process GCLI
*/
var items = [
// First we need to add the local types which other types depend on
require('../types/delegate').items,
require('../types/selection').items,
require('../types/array').items,

require('../types/boolean').items,
require('../types/command').items,
require('../types/date').items,
require('../types/file').items,
require('../types/javascript').items,
require('../types/node').items,
require('../types/number').items,
require('../types/resource').items,
require('../types/setting').items,
require('../types/string').items,
require('../types/union').items,
require('../types/url').items,

require('../fields/fields').items,
require('../fields/delegate').items,
require('../fields/selection').items,

require('../ui/intro').items,
require('../ui/focus').items,

require('../converters/converters').items,
require('../converters/basic').items,
require('../converters/html').items,
require('../converters/terminal').items,

require('../languages/command').items,
require('../languages/javascript').items,

require('./direct').items,
// require('./rdp').items, // Firefox remote debug protocol
require('./websocket').items,
require('./xhr').items,

require('../commands/context').items,

].reduce(function(prev, curr) { return prev.concat(curr); }, []);

/**
* These are the commands stored on the remote side that have converters which
* we'll need to present the data
*/
var requiredConverters = [
require('../cli').items,

require('../commands/clear').items,
require('../commands/connect').items,
require('../commands/exec').items,
require('../commands/global').items,
require('../commands/help').items,
require('../commands/intro').items,
require('../commands/lang').items,
require('../commands/preflist').items,
require('../commands/pref').items,
require('../commands/test').items,

].reduce(function(prev, curr) { return prev.concat(curr); }, [])
.filter(function(item) { return item.item === 'converter'; });

/**
* Connect to a remote system and setup the commands/types/converters etc needed
* to make it all work
*/
exports.connect = function(options) {
exports.createSystem = function(options) {
options = options || {};

var system = api.createSystem();

// Ugly hack, to aid testing
exports.api = system;

options.types = system.types = new Types();
options.commands = system.commands = new Commands(system.types);
var system = createSystem();

// The items that are always needed on the client
var items = [
require('../items/basic').items,
require('../items/ui').items,
require('../items/remote').items,
// The context command makes no sense on the server
require('../commands/context').items,
].reduce(function(prev, curr) { return prev.concat(curr); }, []);
system.addItems(items);
system.addItems(requiredConverters);

var connector = system.connectors.get(options.connector);
return connector.connect(options.url).then(function(connection) {
options.connection = connection;
connection.on('commandsChanged', function(specs) {
exports.addItems(system, specs, connection);
});

return connection.call('specs').then(function(specs) {
exports.addItems(system, specs, connection);
return connection;
});
});
};

exports.addItems = function(system, specs, connection) {
exports.removeRemoteItems(system, connection);
var remoteItems = exports.addLocalFunctions(specs, connection);
system.addItems(remoteItems);
};

/**
* Take the data from the 'specs' command (or the 'commandsChanged' event) and
* add function to proxy the execution back over the connection
*/
exports.addLocalFunctions = function(specs, connection) {
// Inject an 'exec' function into the commands, and the connection into
// all the remote types
specs.forEach(function(commandSpec) {
//
commandSpec.connection = connection;
commandSpec.params.forEach(function(param) {
param.type.connection = connection;
});

if (!commandSpec.isParent) {
commandSpec.exec = function(args, context) {
var data = {
typed: (context.prefix ? context.prefix + ' ' : '') + context.typed
};

return connection.call('execute', data).then(function(reply) {
var typedData = context.typedData(reply.type, reply.data);
if (!reply.error) {
return typedData;
}
else {
throw typedData;
}
});
};
}

commandSpec.isProxy = true;
});

return specs;
};
// These are the commands stored on the remote side that have converters which
// we'll need to present the data. Ideally front.specs() would transfer these,
// that doesn't happen yet so we add them manually
var requiredConverters = [
require('../cli').items,
require('../commands/clear').items,
require('../commands/connect').items,
require('../commands/exec').items,
require('../commands/global').items,
require('../commands/help').items,
require('../commands/intro').items,
require('../commands/lang').items,
require('../commands/preflist').items,
require('../commands/pref').items,
require('../commands/test').items,
].reduce(function(prev, curr) { return prev.concat(curr); }, [])
.filter(function(item) { return item.item === 'converter'; });
system.addItems(requiredConverters);

exports.removeRemoteItems = function(system, connection) {
system.commands.getAll().forEach(function(command) {
if (command.connection === connection) {
system.commands.remove(command);
}
var connector = system.connectors.get(options.method);
return connectSystems(system, connector, options.url).then(function() {
return system;
});
};
Loading

2 comments on commit 517c9a9

@bgrins
Copy link

@bgrins bgrins commented on 517c9a9 Feb 17, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I be reviewing these changes?

@joewalker
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please.
It occurs to me that I can make a pull request from arbitrary commit ranges with some custom branches. So ... joewalker/gcli#76

Please sign in to comment.