summaryrefslogtreecommitdiffstats
path: root/devtools/shared/protocol/Response.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/shared/protocol/Response.js')
-rw-r--r--devtools/shared/protocol/Response.js119
1 files changed, 119 insertions, 0 deletions
diff --git a/devtools/shared/protocol/Response.js b/devtools/shared/protocol/Response.js
new file mode 100644
index 0000000000..b456fe6149
--- /dev/null
+++ b/devtools/shared/protocol/Response.js
@@ -0,0 +1,119 @@
+/* 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";
+
+var {
+ findPlaceholders,
+ getPath,
+} = require("resource://devtools/shared/protocol/utils.js");
+var { types } = require("resource://devtools/shared/protocol/types.js");
+
+/**
+ * Manages a response template.
+ *
+ * @param object template
+ * The response template.
+ * @construcor
+ */
+var Response = function (template = {}) {
+ this.template = template;
+ if (this.template instanceof RetVal && this.template.isArrayType()) {
+ throw Error("Arrays should be wrapped in objects");
+ }
+
+ const placeholders = findPlaceholders(template, RetVal);
+ if (placeholders.length > 1) {
+ throw Error("More than one RetVal specified in response");
+ }
+ const placeholder = placeholders.shift();
+ if (placeholder) {
+ this.retVal = placeholder.placeholder;
+ this.path = placeholder.path;
+ }
+};
+
+Response.prototype = {
+ /**
+ * Write a response for the given return value.
+ *
+ * @param val ret
+ * The return value.
+ * @param object ctx
+ * The object writing the response.
+ */
+ write(ret, ctx) {
+ // Consider that `template` is either directly a `RetVal`,
+ // or a dictionary with may be one `RetVal`.
+ if (this.template instanceof RetVal) {
+ return this.template.write(ret, ctx);
+ }
+ const result = {};
+ for (const key in this.template) {
+ const value = this.template[key];
+ if (value instanceof RetVal) {
+ result[key] = value.write(ret, ctx);
+ } else {
+ throw new Error(
+ "Response can only be a `RetVal` instance or an object " +
+ "with one property being a `RetVal` instance."
+ );
+ }
+ }
+ return result;
+ },
+
+ /**
+ * Read a return value from the given response.
+ *
+ * @param object packet
+ * The response packet.
+ * @param object ctx
+ * The object reading the response.
+ */
+ read(packet, ctx) {
+ if (!this.retVal) {
+ return undefined;
+ }
+ const v = getPath(packet, this.path);
+ return this.retVal.read(v, ctx);
+ },
+};
+
+exports.Response = Response;
+
+/**
+ * Placeholder for return values in a response template.
+ *
+ * @param type type
+ * The return value should be marshalled as this type.
+ */
+var RetVal = function (type) {
+ this._type = type;
+ // Prevent force loading all RetVal types by accessing it only when needed
+ loader.lazyGetter(this, "type", function () {
+ return types.getType(type);
+ });
+};
+
+RetVal.prototype = {
+ write(v, ctx) {
+ return this.type.write(v, ctx);
+ },
+
+ read(v, ctx) {
+ return this.type.read(v, ctx);
+ },
+
+ isArrayType() {
+ // `_type` should always be a string, but a few incorrect RetVal calls
+ // pass `0`. See Bug 1677703.
+ return typeof this._type === "string" && this._type.startsWith("array:");
+ },
+};
+
+// Outside of protocol.js, RetVal is called as factory method, without the new keyword.
+exports.RetVal = function (type) {
+ return new RetVal(type);
+};