/* 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://gre/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); }, });