diff options
Diffstat (limited to 'devtools/shared/protocol/Response.js')
-rw-r--r-- | devtools/shared/protocol/Response.js | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/devtools/shared/protocol/Response.js b/devtools/shared/protocol/Response.js new file mode 100644 index 0000000000..ef6b911fce --- /dev/null +++ b/devtools/shared/protocol/Response.js @@ -0,0 +1,116 @@ +/* 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("devtools/shared/protocol/utils"); +var { types } = require("devtools/shared/protocol/types"); + +/** + * 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: function(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: function(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: function(v, ctx) { + return this.type.write(v, ctx); + }, + + read: function(v, ctx) { + return this.type.read(v, ctx); + }, + + isArrayType: function() { + // `_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); +}; |