Response.js (3112B)
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 { 8 findPlaceholders, 9 getPath, 10 } = require("resource://devtools/shared/protocol/utils.js"); 11 var { types } = require("resource://devtools/shared/protocol/types.js"); 12 13 /** 14 * Manages a response template. 15 */ 16 class Response { 17 /** 18 * @param {object} template 19 * The response template. 20 */ 21 constructor(template = {}) { 22 this.template = template; 23 if (this.template instanceof RetVal && this.template.isArrayType()) { 24 throw Error("Arrays should be wrapped in objects"); 25 } 26 27 const placeholders = findPlaceholders(template, RetVal); 28 if (placeholders.length > 1) { 29 throw Error("More than one RetVal specified in response"); 30 } 31 const placeholder = placeholders.shift(); 32 if (placeholder) { 33 this.retVal = placeholder.placeholder; 34 this.path = placeholder.path; 35 } 36 } 37 /** 38 * Write a response for the given return value. 39 * 40 * @param val ret 41 * The return value. 42 * @param {object} ctx 43 * The object writing the response. 44 */ 45 write(ret, ctx) { 46 // Consider that `template` is either directly a `RetVal`, 47 // or a dictionary with may be one `RetVal`. 48 if (this.template instanceof RetVal) { 49 return this.template.write(ret, ctx); 50 } 51 const result = {}; 52 for (const key in this.template) { 53 const value = this.template[key]; 54 if (value instanceof RetVal) { 55 result[key] = value.write(ret, ctx); 56 } else { 57 throw new Error( 58 "Response can only be a `RetVal` instance or an object " + 59 "with one property being a `RetVal` instance." 60 ); 61 } 62 } 63 return result; 64 } 65 66 /** 67 * Read a return value from the given response. 68 * 69 * @param {object} packet 70 * The response packet. 71 * @param {object} ctx 72 * The object reading the response. 73 */ 74 read(packet, ctx) { 75 if (!this.retVal) { 76 return undefined; 77 } 78 const v = getPath(packet, this.path); 79 return this.retVal.read(v, ctx); 80 } 81 } 82 83 exports.Response = Response; 84 85 /** 86 * Placeholder for return values in a response template. 87 */ 88 class RetVal { 89 /** 90 * @param type type 91 * The return value should be marshalled as this type. 92 */ 93 constructor(type) { 94 this._type = type; 95 // Prevent force loading all RetVal types by accessing it only when needed 96 loader.lazyGetter(this, "type", function () { 97 return types.getType(type); 98 }); 99 } 100 write(v, ctx) { 101 return this.type.write(v, ctx); 102 } 103 104 read(v, ctx) { 105 return this.type.read(v, ctx); 106 } 107 108 isArrayType() { 109 // `_type` should always be a string, but a few incorrect RetVal calls 110 // pass `0`. See Bug 1677703. 111 return typeof this._type === "string" && this._type.startsWith("array:"); 112 } 113 } 114 115 // Outside of protocol.js, RetVal is called as factory method, without the new keyword. 116 exports.RetVal = function (type) { 117 return new RetVal(type); 118 };