gecko-dev/toolkit/devtools/server/actors/gcli.js

208 lines
6.4 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var Cu = require('chrome').Cu;
var XPCOMUtils = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}).XPCOMUtils;
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CommandUtils",
"resource:///modules/devtools/DeveloperToolbar.jsm");
XPCOMUtils.defineLazyGetter(this, "Requisition", function() {
return require("gcli/cli").Requisition;
});
XPCOMUtils.defineLazyGetter(this, "centralCanon", function() {
return require("gcli/commands/commands").centralCanon;
});
var util = require('gcli/util/util');
var protocol = require("devtools/server/protocol");
var method = protocol.method;
var Arg = protocol.Arg;
var Option = protocol.Option;
var RetVal = protocol.RetVal;
/**
* Manage remote connections that want to talk to GCLI
*/
var GcliActor = exports.GcliActor = protocol.ActorClass({
typeName: "gcli",
initialize: function(conn, tabActor) {
protocol.Actor.prototype.initialize.call(this, conn);
this.tabActor = tabActor;
let browser = tabActor.browser;
let environment = {
chromeWindow: browser.ownerGlobal,
chromeDocument: browser.ownerDocument,
window: browser.contentWindow,
document: browser.contentDocument
};
this.requisition = new Requisition({ environment: env });
},
/**
* Retrieve a list of the remotely executable commands
*/
specs: method(function() {
return this.requisition.canon.getCommandSpecs();
}, {
request: {},
response: RetVal("json")
}),
/**
* Execute a GCLI command
* @return a promise of an object with the following properties:
* - data: The output of the command
* - type: The type of the data to allow selection of a converter
* - error: True if the output was considered an error
*/
execute: method(function(typed) {
return this.requisition.updateExec(typed).then(function(output) {
return output.toJson();
});
}, {
request: {
typed: Arg(0, "string") // The command string
},
response: RetVal("json")
}),
/**
* Get the state of an input string. i.e. requisition.getStateData()
*/
state: method(function(typed, start, rank) {
return this.requisition.update(typed).then(() => {
return this.requisition.getStateData(start, rank);
});
}, {
request: {
typed: Arg(0, "string"), // The command string
start: Arg(1, "number"), // Cursor start position
rank: Arg(2, "number") // The prediction offset (# times UP/DOWN pressed)
},
response: RetVal("json")
}),
/**
* Call type.parse to check validity. Used by the remote type
* @return a promise of an object with the following properties:
* - status: Of of the following strings: VALID|INCOMPLETE|ERROR
* - message: The message to display to the user
* - predictions: An array of suggested values for the given parameter
*/
typeparse: method(function(typed, param) {
return this.requisition.update(typed).then(function() {
var assignment = this.requisition.getAssignment(param);
return promise.resolve(assignment.predictions).then(function(predictions) {
return {
status: assignment.getStatus().toString(),
message: assignment.message,
predictions: predictions
};
});
});
}, {
request: {
typed: Arg(0, "string"), // The command string
param: Arg(1, "string") // The name of the parameter to parse
},
response: RetVal("json")
}),
/**
* Get the incremented value of some type
* @return a promise of a string containing the new argument text
*/
typeincrement: method(function(typed, param) {
return this.requisition.update(typed).then(function() {
var assignment = this.requisition.getAssignment(param);
return this.requisition.increment(assignment).then(function() {
return assignment.arg == null ? undefined : assignment.arg.text;
});
});
}, {
request: {
typed: Arg(0, "string"), // The command string
param: Arg(1, "string") // The name of the parameter to parse
},
response: RetVal("string")
}),
/**
* See typeincrement
*/
typedecrement: method(function(typed, param) {
return this.requisition.update(typed).then(function() {
var assignment = this.requisition.getAssignment(param);
return this.requisition.decrement(assignment).then(function() {
return assignment.arg == null ? undefined : assignment.arg.text;
});
});
}, {
request: {
typed: Arg(0, "string"), // The command string
param: Arg(1, "string") // The name of the parameter to parse
},
response: RetVal("string")
}),
/**
* Perform a lookup on a selection type to get the allowed values
*/
selectioninfo: method(function(commandName, paramName, action) {
var command = this.requisition.canon.getCommand(commandName);
if (command == null) {
throw new Error('No command called \'' + commandName + '\'');
}
var type;
command.params.forEach(function(param) {
if (param.name === paramName) {
type = param.type;
}
});
if (type == null) {
throw new Error('No parameter called \'' + paramName + '\' in \'' +
commandName + '\'');
}
switch (action) {
case 'lookup':
return type.lookup(context);
case 'data':
return type.data(context);
default:
throw new Error('Action must be either \'lookup\' or \'data\'');
}
}, {
request: {
typed: Arg(0, "string"), // The command containing the parameter in question
param: Arg(1, "string"), // The name of the parameter
action: Arg(1, "string") // 'lookup' or 'data' depending on the function to call
},
response: RetVal("json")
})
});
exports.GcliFront = protocol.FrontClass(GcliActor, {
initialize: function(client, tabForm) {
protocol.Front.prototype.initialize.call(this, client);
this.actorID = tabForm.gcliActor;
// XXX: This is the first actor type in its hierarchy to use the protocol
// library, so we're going to self-own on the client side for now.
this.manage(this);
},
});