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