1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/devtools/server/actors/gcli.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,219 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +var Cu = require('chrome').Cu; 1.11 +var XPCOMUtils = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}).XPCOMUtils; 1.12 + 1.13 +XPCOMUtils.defineLazyModuleGetter(this, "console", 1.14 + "resource://gre/modules/devtools/Console.jsm"); 1.15 +XPCOMUtils.defineLazyModuleGetter(this, "CommandUtils", 1.16 + "resource:///modules/devtools/DeveloperToolbar.jsm"); 1.17 + 1.18 +XPCOMUtils.defineLazyGetter(this, "Requisition", function() { 1.19 + return require("gcli/cli").Requisition; 1.20 +}); 1.21 + 1.22 +XPCOMUtils.defineLazyGetter(this, "centralCanon", function() { 1.23 + return require("gcli/commands/commands").centralCanon; 1.24 +}); 1.25 + 1.26 +var util = require('gcli/util/util'); 1.27 + 1.28 +var protocol = require("devtools/server/protocol"); 1.29 +var method = protocol.method; 1.30 +var Arg = protocol.Arg; 1.31 +var Option = protocol.Option; 1.32 +var RetVal = protocol.RetVal; 1.33 + 1.34 +/** 1.35 + * Manage remote connections that want to talk to GCLI 1.36 + */ 1.37 +var GcliActor = protocol.ActorClass({ 1.38 + typeName: "gcli", 1.39 + 1.40 + initialize: function(conn, tabActor) { 1.41 + protocol.Actor.prototype.initialize.call(this, conn); 1.42 + this.tabActor = tabActor; 1.43 + let browser = tabActor.browser; 1.44 + 1.45 + let environment = { 1.46 + chromeWindow: browser.ownerGlobal, 1.47 + chromeDocument: browser.ownerDocument, 1.48 + window: browser.contentWindow, 1.49 + document: browser.contentDocument 1.50 + }; 1.51 + 1.52 + this.requisition = new Requisition({ environment: env }); 1.53 + }, 1.54 + 1.55 + /** 1.56 + * Retrieve a list of the remotely executable commands 1.57 + */ 1.58 + specs: method(function() { 1.59 + return this.requisition.canon.getCommandSpecs(); 1.60 + }, { 1.61 + request: {}, 1.62 + response: RetVal("json") 1.63 + }), 1.64 + 1.65 + /** 1.66 + * Execute a GCLI command 1.67 + * @return a promise of an object with the following properties: 1.68 + * - data: The output of the command 1.69 + * - type: The type of the data to allow selection of a converter 1.70 + * - error: True if the output was considered an error 1.71 + */ 1.72 + execute: method(function(typed) { 1.73 + return this.requisition.updateExec(typed).then(function(output) { 1.74 + return output.toJson(); 1.75 + }); 1.76 + }, { 1.77 + request: { 1.78 + typed: Arg(0, "string") // The command string 1.79 + }, 1.80 + response: RetVal("json") 1.81 + }), 1.82 + 1.83 + /** 1.84 + * Get the state of an input string. i.e. requisition.getStateData() 1.85 + */ 1.86 + state: method(function(typed, start, rank) { 1.87 + return this.requisition.update(typed).then(function() { 1.88 + return this.requisition.getStateData(start, rank); 1.89 + }.bind(this)); 1.90 + }, { 1.91 + request: { 1.92 + typed: Arg(0, "string"), // The command string 1.93 + start: Arg(1, "number"), // Cursor start position 1.94 + rank: Arg(2, "number") // The prediction offset (# times UP/DOWN pressed) 1.95 + }, 1.96 + response: RetVal("json") 1.97 + }), 1.98 + 1.99 + /** 1.100 + * Call type.parse to check validity. Used by the remote type 1.101 + * @return a promise of an object with the following properties: 1.102 + * - status: Of of the following strings: VALID|INCOMPLETE|ERROR 1.103 + * - message: The message to display to the user 1.104 + * - predictions: An array of suggested values for the given parameter 1.105 + */ 1.106 + typeparse: method(function(typed, param) { 1.107 + return this.requisition.update(typed).then(function() { 1.108 + var assignment = this.requisition.getAssignment(param); 1.109 + 1.110 + return promise.resolve(assignment.predictions).then(function(predictions) { 1.111 + return { 1.112 + status: assignment.getStatus().toString(), 1.113 + message: assignment.message, 1.114 + predictions: predictions 1.115 + }; 1.116 + }); 1.117 + }); 1.118 + }, { 1.119 + request: { 1.120 + typed: Arg(0, "string"), // The command string 1.121 + param: Arg(1, "string") // The name of the parameter to parse 1.122 + }, 1.123 + response: RetVal("json") 1.124 + }), 1.125 + 1.126 + /** 1.127 + * Get the incremented value of some type 1.128 + * @return a promise of a string containing the new argument text 1.129 + */ 1.130 + typeincrement: method(function(typed, param) { 1.131 + return this.requisition.update(typed).then(function() { 1.132 + var assignment = this.requisition.getAssignment(param); 1.133 + return this.requisition.increment(assignment).then(function() { 1.134 + return assignment.arg == null ? undefined : assignment.arg.text; 1.135 + }); 1.136 + }); 1.137 + }, { 1.138 + request: { 1.139 + typed: Arg(0, "string"), // The command string 1.140 + param: Arg(1, "string") // The name of the parameter to parse 1.141 + }, 1.142 + response: RetVal("string") 1.143 + }), 1.144 + 1.145 + /** 1.146 + * See typeincrement 1.147 + */ 1.148 + typedecrement: method(function(typed, param) { 1.149 + return this.requisition.update(typed).then(function() { 1.150 + var assignment = this.requisition.getAssignment(param); 1.151 + return this.requisition.decrement(assignment).then(function() { 1.152 + return assignment.arg == null ? undefined : assignment.arg.text; 1.153 + }); 1.154 + }); 1.155 + }, { 1.156 + request: { 1.157 + typed: Arg(0, "string"), // The command string 1.158 + param: Arg(1, "string") // The name of the parameter to parse 1.159 + }, 1.160 + response: RetVal("string") 1.161 + }), 1.162 + 1.163 + /** 1.164 + * Perform a lookup on a selection type to get the allowed values 1.165 + */ 1.166 + selectioninfo: method(function(commandName, paramName, action) { 1.167 + var command = this.requisition.canon.getCommand(commandName); 1.168 + if (command == null) { 1.169 + throw new Error('No command called \'' + commandName + '\''); 1.170 + } 1.171 + 1.172 + var type; 1.173 + command.params.forEach(function(param) { 1.174 + if (param.name === paramName) { 1.175 + type = param.type; 1.176 + } 1.177 + }); 1.178 + if (type == null) { 1.179 + throw new Error('No parameter called \'' + paramName + '\' in \'' + 1.180 + commandName + '\''); 1.181 + } 1.182 + 1.183 + switch (action) { 1.184 + case 'lookup': 1.185 + return type.lookup(context); 1.186 + case 'data': 1.187 + return type.data(context); 1.188 + default: 1.189 + throw new Error('Action must be either \'lookup\' or \'data\''); 1.190 + } 1.191 + }, { 1.192 + request: { 1.193 + typed: Arg(0, "string"), // The command containing the parameter in question 1.194 + param: Arg(1, "string"), // The name of the parameter 1.195 + action: Arg(1, "string") // 'lookup' or 'data' depending on the function to call 1.196 + }, 1.197 + response: RetVal("json") 1.198 + }) 1.199 +}); 1.200 + 1.201 +exports.GcliFront = protocol.FrontClass(GcliActor, { 1.202 + initialize: function(client, tabForm) { 1.203 + protocol.Front.prototype.initialize.call(this, client); 1.204 + this.actorID = tabForm.gcliActor; 1.205 + 1.206 + // XXX: This is the first actor type in its hierarchy to use the protocol 1.207 + // library, so we're going to self-own on the client side for now. 1.208 + client.addActorPool(this); 1.209 + this.manage(this); 1.210 + }, 1.211 +}); 1.212 + 1.213 +/** 1.214 + * Called the framework on DebuggerServer.registerModule() 1.215 + */ 1.216 +exports.register = function(handle) { 1.217 + handle.addTabActor(GcliActor, "gcliActor"); 1.218 +}; 1.219 + 1.220 +exports.unregister = function(handle) { 1.221 + handle.removeTabActor(GcliActor); 1.222 +};