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 file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 ChromeUtils.defineESModuleGetters(lazy, {
8 assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
9 error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
10 truncate: "chrome://remote/content/shared/Format.sys.mjs",
13 /** Representation of the packets transproted over the wire. */
14 export class Message {
16 * @param {number} messageID
17 * Message ID unique identifying this message.
19 constructor(messageID) {
20 this.id = lazy.assert.integer(messageID);
24 function replacer(key, value) {
25 if (typeof value === "string") {
26 return lazy.truncate`${value}`;
31 return JSON.stringify(this.toPacket(), replacer);
35 * Converts a data packet into a {@link Command} or {@link Response}.
37 * @param {Array.<number, number, ?, ?>} data
38 * A four element array where the elements, in sequence, signifies
39 * message type, message ID, method name or error, and parameters
43 * Based on the message type, a {@link Command} or {@link Response}
47 * If the message type is not recognised.
49 static fromPacket(data) {
54 return Command.fromPacket(data);
57 return Response.fromPacket(data);
61 "Unrecognised message type in packet: " + JSON.stringify(data)
68 * Messages may originate from either the server or the client.
69 * Because the remote protocol is full duplex, both endpoints may be
70 * the origin of both commands and responses.
73 * @see {@link Message}
76 /** Indicates that the message originates from the client. */
78 /** Indicates that the message originates from the server. */
83 * A command is a request from the client to run a series of remote end
84 * steps and return a fitting response.
86 * The command can be synthesised from the message passed over the
87 * Marionette socket using the {@link fromPacket} function. The format of
91 * [<var>type</var>, <var>id</var>, <var>name</var>, <var>params</var>]
97 * <dt><var>type</var> (integer)
99 * Must be zero (integer). Zero means that this message is
102 * <dt><var>id</var> (integer)
104 * Integer used as a sequence number. The server replies with
105 * the same ID for the response.
107 * <dt><var>name</var> (string)
109 * String representing the command name with an associated set
110 * of remote end steps.
112 * <dt><var>params</var> (JSON Object or null)
114 * Object of command function arguments. The keys of this object
115 * must be strings, but the values can be arbitrary values.
118 * A command has an associated message <var>id</var> that prevents
119 * the dispatcher from sending responses in the wrong order.
121 * The command may also have optional error- and result handlers that
122 * are called when the client returns with a response. These are
123 * <code>function onerror({Object})</code>,
124 * <code>function onresult({Object})</code>, and
125 * <code>function onresult({Response})</code>:
127 * @param {number} messageID
128 * Message ID unique identifying this message.
129 * @param {string} name
131 * @param {Object<string, ?>} params
132 * Command parameters.
134 export class Command extends Message {
135 constructor(messageID, name, params = {}) {
138 this.name = lazy.assert.string(name);
139 this.parameters = lazy.assert.object(params);
142 this.onresult = null;
144 this.origin = Message.Origin.Client;
149 * Calls the error- or result handler associated with this command.
150 * This function can be replaced with a custom response handler.
152 * @param {Response} resp
153 * The response to pass on to the result or error to the
154 * <code>onerror</code> or <code>onresult</code> handlers to.
157 if (this.onerror && resp.error) {
158 this.onerror(resp.error);
159 } else if (this.onresult && resp.body) {
160 this.onresult(resp.body);
165 * Encodes the command to a packet.
171 return [Command.Type, this.id, this.name, this.parameters];
175 * Converts a data packet into {@link Command}.
177 * @param {Array.<number, number, *, *>} payload
178 * A four element array where the elements, in sequence, signifies
179 * message type, message ID, command name, and parameters.
182 * Representation of packet.
184 * @throws {TypeError}
185 * If the message type is not recognised.
187 static fromPacket(payload) {
188 let [type, msgID, name, params] = payload;
189 lazy.assert.that(n => n === Command.Type)(type);
191 // if parameters are given but null, treat them as undefined
192 if (params === null) {
196 return new Command(msgID, name, params);
203 * @callback ResponseCallback
205 * @param {Response} resp
206 * Response to handle.
210 * Represents the response returned from the remote end after execution
211 * of its corresponding command.
213 * The response is a mutable object passed to each command for
214 * modification through the available setters. To send data in a response,
215 * you modify the body property on the response. The body property can
216 * also be replaced completely.
218 * The response is sent implicitly by
219 * {@link server.TCPConnection#execute when a command has finished
220 * executing, and any modifications made subsequent to that will have
223 * @param {number} messageID
224 * Message ID tied to the corresponding command request this is
226 * @param {ResponseHandler} respHandler
227 * Function callback called on sending the response.
229 export class Response extends Message {
230 constructor(messageID, respHandler = () => {}) {
233 this.respHandler_ = lazy.assert.callable(respHandler);
236 this.body = { value: null };
238 this.origin = Message.Origin.Server;
243 * Sends response conditionally, given a predicate.
245 * @param {function(Response): boolean} predicate
246 * A predicate taking a Response object and returning a boolean.
248 sendConditionally(predicate) {
249 if (predicate(this)) {
255 * Sends response using the response handler provided on
258 * @throws {RangeError}
259 * If the response has already been sent.
263 throw new RangeError("Response has already been sent: " + this);
265 this.respHandler_(this);
270 * Send error to client.
272 * Turns the response into an error response, clears any previously
273 * set body data, and sends it using the response handler provided
277 * The Error instance to send.
280 * If <var>err</var> is not a {@link WebDriverError}, the error
281 * is propagated, i.e. rethrown.
284 this.error = lazy.error.wrap(err).toJSON();
288 // propagate errors which are implementation problems
289 if (!lazy.error.isWebDriverError(err)) {
295 * Encodes the response to a packet.
301 return [Response.Type, this.id, this.error, this.body];
305 * Converts a data packet into {@link Response}.
307 * @param {Array.<number, number, ?, ?>} payload
308 * A four element array where the elements, in sequence, signifies
309 * message type, message ID, error, and result.
311 * @returns {Response}
312 * Representation of packet.
314 * @throws {TypeError}
315 * If the message type is not recognised.
317 static fromPacket(payload) {
318 let [type, msgID, err, body] = payload;
319 lazy.assert.that(n => n === Response.Type)(type);
321 let resp = new Response(msgID);
322 resp.error = lazy.assert.string(err);