summaryrefslogtreecommitdiffstats
path: root/devtools/shared/protocol/Request.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--devtools/shared/protocol/Request.js169
1 files changed, 169 insertions, 0 deletions
diff --git a/devtools/shared/protocol/Request.js b/devtools/shared/protocol/Request.js
new file mode 100644
index 0000000000..20befd938a
--- /dev/null
+++ b/devtools/shared/protocol/Request.js
@@ -0,0 +1,169 @@
+/* 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";
+
+const { extend } = require("resource://devtools/shared/extend.js");
+var {
+ findPlaceholders,
+ getPath,
+} = require("resource://devtools/shared/protocol/utils.js");
+var { types } = require("resource://devtools/shared/protocol/types.js");
+
+/**
+ * Manages a request template.
+ *
+ * @param object template
+ * The request template.
+ * @construcor
+ */
+var Request = function (template = {}) {
+ this.type = template.type;
+ this.template = template;
+ this.args = findPlaceholders(template, Arg);
+};
+
+Request.prototype = {
+ /**
+ * Write a request.
+ *
+ * @param array fnArgs
+ * The function arguments to place in the request.
+ * @param object ctx
+ * The object making the request.
+ * @returns a request packet.
+ */
+ write(fnArgs, ctx) {
+ const ret = {};
+ for (const key in this.template) {
+ const value = this.template[key];
+ if (value instanceof Arg) {
+ ret[key] = value.write(
+ value.index in fnArgs ? fnArgs[value.index] : undefined,
+ ctx,
+ key
+ );
+ } else if (key == "type") {
+ ret[key] = value;
+ } else {
+ throw new Error(
+ "Request can only an object with `Arg` or `Option` properties"
+ );
+ }
+ }
+ return ret;
+ },
+
+ /**
+ * Read a request.
+ *
+ * @param object packet
+ * The request packet.
+ * @param object ctx
+ * The object making the request.
+ * @returns an arguments array
+ */
+ read(packet, ctx) {
+ const fnArgs = [];
+ for (const templateArg of this.args) {
+ const arg = templateArg.placeholder;
+ const path = templateArg.path;
+ const name = path[path.length - 1];
+ arg.read(getPath(packet, path), ctx, fnArgs, name);
+ }
+ return fnArgs;
+ },
+};
+
+exports.Request = Request;
+
+/**
+ * Request/Response templates and generation
+ *
+ * Request packets are specified as json templates with
+ * Arg and Option placeholders where arguments should be
+ * placed.
+ *
+ * Reponse packets are also specified as json templates,
+ * with a RetVal placeholder where the return value should be
+ * placed.
+ */
+
+/**
+ * Placeholder for simple arguments.
+ *
+ * @param number index
+ * The argument index to place at this position.
+ * @param type type
+ * The argument should be marshalled as this type.
+ * @constructor
+ */
+var Arg = function (index, type) {
+ this.index = index;
+ // Prevent force loading all Arg types by accessing it only when needed
+ loader.lazyGetter(this, "type", function () {
+ return types.getType(type);
+ });
+};
+
+Arg.prototype = {
+ write(arg, ctx) {
+ return this.type.write(arg, ctx);
+ },
+
+ read(v, ctx, outArgs) {
+ outArgs[this.index] = this.type.read(v, ctx);
+ },
+};
+
+// Outside of protocol.js, Arg is called as factory method, without the new keyword.
+exports.Arg = function (index, type) {
+ return new Arg(index, type);
+};
+
+/**
+ * Placeholder for an options argument value that should be hoisted
+ * into the packet.
+ *
+ * If provided in a method specification:
+ *
+ * { optionArg: Option(1)}
+ *
+ * Then arguments[1].optionArg will be placed in the packet in this
+ * value's place.
+ *
+ * @param number index
+ * The argument index of the options value.
+ * @param type type
+ * The argument should be marshalled as this type.
+ * @constructor
+ */
+var Option = function (index, type) {
+ Arg.call(this, index, type);
+};
+
+Option.prototype = extend(Arg.prototype, {
+ write(arg, ctx, name) {
+ // Ignore if arg is undefined or null; allow other falsy values
+ if (arg == undefined || arg[name] == undefined) {
+ return undefined;
+ }
+ const v = arg[name];
+ return this.type.write(v, ctx);
+ },
+ read(v, ctx, outArgs, name) {
+ if (outArgs[this.index] === undefined) {
+ outArgs[this.index] = {};
+ }
+ if (v === undefined) {
+ return;
+ }
+ outArgs[this.index][name] = this.type.read(v, ctx);
+ },
+});
+
+// Outside of protocol.js, Option is called as factory method, without the new keyword.
+exports.Option = function (index, type) {
+ return new Option(index, type);
+};