|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 var Cu = require('chrome').Cu; |
|
8 var XPCOMUtils = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}).XPCOMUtils; |
|
9 |
|
10 XPCOMUtils.defineLazyModuleGetter(this, "console", |
|
11 "resource://gre/modules/devtools/Console.jsm"); |
|
12 XPCOMUtils.defineLazyModuleGetter(this, "CommandUtils", |
|
13 "resource:///modules/devtools/DeveloperToolbar.jsm"); |
|
14 |
|
15 XPCOMUtils.defineLazyGetter(this, "Requisition", function() { |
|
16 return require("gcli/cli").Requisition; |
|
17 }); |
|
18 |
|
19 XPCOMUtils.defineLazyGetter(this, "centralCanon", function() { |
|
20 return require("gcli/commands/commands").centralCanon; |
|
21 }); |
|
22 |
|
23 var util = require('gcli/util/util'); |
|
24 |
|
25 var protocol = require("devtools/server/protocol"); |
|
26 var method = protocol.method; |
|
27 var Arg = protocol.Arg; |
|
28 var Option = protocol.Option; |
|
29 var RetVal = protocol.RetVal; |
|
30 |
|
31 /** |
|
32 * Manage remote connections that want to talk to GCLI |
|
33 */ |
|
34 var GcliActor = protocol.ActorClass({ |
|
35 typeName: "gcli", |
|
36 |
|
37 initialize: function(conn, tabActor) { |
|
38 protocol.Actor.prototype.initialize.call(this, conn); |
|
39 this.tabActor = tabActor; |
|
40 let browser = tabActor.browser; |
|
41 |
|
42 let environment = { |
|
43 chromeWindow: browser.ownerGlobal, |
|
44 chromeDocument: browser.ownerDocument, |
|
45 window: browser.contentWindow, |
|
46 document: browser.contentDocument |
|
47 }; |
|
48 |
|
49 this.requisition = new Requisition({ environment: env }); |
|
50 }, |
|
51 |
|
52 /** |
|
53 * Retrieve a list of the remotely executable commands |
|
54 */ |
|
55 specs: method(function() { |
|
56 return this.requisition.canon.getCommandSpecs(); |
|
57 }, { |
|
58 request: {}, |
|
59 response: RetVal("json") |
|
60 }), |
|
61 |
|
62 /** |
|
63 * Execute a GCLI command |
|
64 * @return a promise of an object with the following properties: |
|
65 * - data: The output of the command |
|
66 * - type: The type of the data to allow selection of a converter |
|
67 * - error: True if the output was considered an error |
|
68 */ |
|
69 execute: method(function(typed) { |
|
70 return this.requisition.updateExec(typed).then(function(output) { |
|
71 return output.toJson(); |
|
72 }); |
|
73 }, { |
|
74 request: { |
|
75 typed: Arg(0, "string") // The command string |
|
76 }, |
|
77 response: RetVal("json") |
|
78 }), |
|
79 |
|
80 /** |
|
81 * Get the state of an input string. i.e. requisition.getStateData() |
|
82 */ |
|
83 state: method(function(typed, start, rank) { |
|
84 return this.requisition.update(typed).then(function() { |
|
85 return this.requisition.getStateData(start, rank); |
|
86 }.bind(this)); |
|
87 }, { |
|
88 request: { |
|
89 typed: Arg(0, "string"), // The command string |
|
90 start: Arg(1, "number"), // Cursor start position |
|
91 rank: Arg(2, "number") // The prediction offset (# times UP/DOWN pressed) |
|
92 }, |
|
93 response: RetVal("json") |
|
94 }), |
|
95 |
|
96 /** |
|
97 * Call type.parse to check validity. Used by the remote type |
|
98 * @return a promise of an object with the following properties: |
|
99 * - status: Of of the following strings: VALID|INCOMPLETE|ERROR |
|
100 * - message: The message to display to the user |
|
101 * - predictions: An array of suggested values for the given parameter |
|
102 */ |
|
103 typeparse: method(function(typed, param) { |
|
104 return this.requisition.update(typed).then(function() { |
|
105 var assignment = this.requisition.getAssignment(param); |
|
106 |
|
107 return promise.resolve(assignment.predictions).then(function(predictions) { |
|
108 return { |
|
109 status: assignment.getStatus().toString(), |
|
110 message: assignment.message, |
|
111 predictions: predictions |
|
112 }; |
|
113 }); |
|
114 }); |
|
115 }, { |
|
116 request: { |
|
117 typed: Arg(0, "string"), // The command string |
|
118 param: Arg(1, "string") // The name of the parameter to parse |
|
119 }, |
|
120 response: RetVal("json") |
|
121 }), |
|
122 |
|
123 /** |
|
124 * Get the incremented value of some type |
|
125 * @return a promise of a string containing the new argument text |
|
126 */ |
|
127 typeincrement: method(function(typed, param) { |
|
128 return this.requisition.update(typed).then(function() { |
|
129 var assignment = this.requisition.getAssignment(param); |
|
130 return this.requisition.increment(assignment).then(function() { |
|
131 return assignment.arg == null ? undefined : assignment.arg.text; |
|
132 }); |
|
133 }); |
|
134 }, { |
|
135 request: { |
|
136 typed: Arg(0, "string"), // The command string |
|
137 param: Arg(1, "string") // The name of the parameter to parse |
|
138 }, |
|
139 response: RetVal("string") |
|
140 }), |
|
141 |
|
142 /** |
|
143 * See typeincrement |
|
144 */ |
|
145 typedecrement: method(function(typed, param) { |
|
146 return this.requisition.update(typed).then(function() { |
|
147 var assignment = this.requisition.getAssignment(param); |
|
148 return this.requisition.decrement(assignment).then(function() { |
|
149 return assignment.arg == null ? undefined : assignment.arg.text; |
|
150 }); |
|
151 }); |
|
152 }, { |
|
153 request: { |
|
154 typed: Arg(0, "string"), // The command string |
|
155 param: Arg(1, "string") // The name of the parameter to parse |
|
156 }, |
|
157 response: RetVal("string") |
|
158 }), |
|
159 |
|
160 /** |
|
161 * Perform a lookup on a selection type to get the allowed values |
|
162 */ |
|
163 selectioninfo: method(function(commandName, paramName, action) { |
|
164 var command = this.requisition.canon.getCommand(commandName); |
|
165 if (command == null) { |
|
166 throw new Error('No command called \'' + commandName + '\''); |
|
167 } |
|
168 |
|
169 var type; |
|
170 command.params.forEach(function(param) { |
|
171 if (param.name === paramName) { |
|
172 type = param.type; |
|
173 } |
|
174 }); |
|
175 if (type == null) { |
|
176 throw new Error('No parameter called \'' + paramName + '\' in \'' + |
|
177 commandName + '\''); |
|
178 } |
|
179 |
|
180 switch (action) { |
|
181 case 'lookup': |
|
182 return type.lookup(context); |
|
183 case 'data': |
|
184 return type.data(context); |
|
185 default: |
|
186 throw new Error('Action must be either \'lookup\' or \'data\''); |
|
187 } |
|
188 }, { |
|
189 request: { |
|
190 typed: Arg(0, "string"), // The command containing the parameter in question |
|
191 param: Arg(1, "string"), // The name of the parameter |
|
192 action: Arg(1, "string") // 'lookup' or 'data' depending on the function to call |
|
193 }, |
|
194 response: RetVal("json") |
|
195 }) |
|
196 }); |
|
197 |
|
198 exports.GcliFront = protocol.FrontClass(GcliActor, { |
|
199 initialize: function(client, tabForm) { |
|
200 protocol.Front.prototype.initialize.call(this, client); |
|
201 this.actorID = tabForm.gcliActor; |
|
202 |
|
203 // XXX: This is the first actor type in its hierarchy to use the protocol |
|
204 // library, so we're going to self-own on the client side for now. |
|
205 client.addActorPool(this); |
|
206 this.manage(this); |
|
207 }, |
|
208 }); |
|
209 |
|
210 /** |
|
211 * Called the framework on DebuggerServer.registerModule() |
|
212 */ |
|
213 exports.register = function(handle) { |
|
214 handle.addTabActor(GcliActor, "gcliActor"); |
|
215 }; |
|
216 |
|
217 exports.unregister = function(handle) { |
|
218 handle.removeTabActor(GcliActor); |
|
219 }; |