FrontClassWithSpec.js (4329B)
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 BULK_REQUEST, 9 BULK_RESPONSE, 10 } = require("resource://devtools/shared/protocol/types.js"); 11 var { Front } = require("resource://devtools/shared/protocol/Front.js"); 12 13 /** 14 * Generates request methods as described by the given actor specification on 15 * the given front prototype. Returns the front prototype. 16 */ 17 var generateRequestMethods = function (actorSpec, frontProto) { 18 if (frontProto._actorSpec) { 19 throw new Error("frontProto called twice on the same front prototype!"); 20 } 21 22 frontProto.typeName = actorSpec.typeName; 23 24 // Generate request methods. 25 const methods = actorSpec.methods; 26 methods.forEach(spec => { 27 const { name } = spec; 28 29 frontProto[name] = function (...args) { 30 // If the front is destroyed, the request will not be able to complete. 31 if (this.isDestroyed()) { 32 throw new Error( 33 `Can not send request '${name}' because front '${this.typeName}' is already destroyed.` 34 ); 35 } 36 37 const startTime = ChromeUtils.now(); 38 let packet; 39 try { 40 packet = spec.request.write(args, this); 41 } catch (ex) { 42 console.error("Error writing request: " + name); 43 throw ex; 44 } 45 if (spec.oneway) { 46 // Fire-and-forget oneway packets. 47 this.send(packet); 48 return undefined; 49 } 50 51 // Check if the client request should be sent as a bulk request 52 const isSendingBulkData = spec.request.template === BULK_REQUEST; 53 54 // If so, pass the last front argument as the bulk initialization callback 55 const clientBulkCallback = isSendingBulkData ? args.at(-1) : null; 56 57 return this.request(packet, { 58 bulk: isSendingBulkData, 59 clientBulkCallback, 60 }).then(response => { 61 // If the request returns bulk data, return the transport response as-is. 62 // We do not expect any custom packet/attributes for bulk responses, 63 // the transport will handle the binary stream communication and expose 64 // the StreamCopier as resolution value in the returned Promise. 65 const isReceivingBulkData = spec.response.template === BULK_RESPONSE; 66 if (isReceivingBulkData) { 67 return response; 68 } 69 70 let ret; 71 if (!this.conn) { 72 throw new Error("Missing conn on " + this); 73 } 74 if (this.isDestroyed()) { 75 throw new Error( 76 `Can not interpret '${name}' response because front '${this.typeName}' is already destroyed.` 77 ); 78 } 79 try { 80 ret = spec.response.read(response, this); 81 } catch (ex) { 82 console.error("Error reading response to: " + name + "\n" + ex); 83 throw ex; 84 } 85 ChromeUtils.addProfilerMarker( 86 "RDP Front", 87 startTime, 88 `${this.typeName}:${name}()` 89 ); 90 return ret; 91 }); 92 }; 93 94 // Release methods should call the destroy function on return. 95 if (spec.release) { 96 const fn = frontProto[name]; 97 frontProto[name] = function (...args) { 98 return fn.apply(this, args).then(result => { 99 this.destroy(); 100 return result; 101 }); 102 }; 103 } 104 }); 105 106 // Process event specifications 107 frontProto._clientSpec = {}; 108 109 const actorEvents = actorSpec.events; 110 if (actorEvents) { 111 frontProto._clientSpec.events = new Map(); 112 113 for (const [name, request] of actorEvents) { 114 frontProto._clientSpec.events.set(request.type, { 115 name, 116 request, 117 }); 118 } 119 } 120 121 frontProto._actorSpec = actorSpec; 122 123 return frontProto; 124 }; 125 126 /** 127 * Create a front class for the given actor specification and front prototype. 128 * 129 * @param object actorSpec 130 * The actor specification you're creating a front for. 131 * @param object proto 132 * The object prototype. Must have a 'typeName' property, 133 * should have method definitions, can have event definitions. 134 */ 135 var FrontClassWithSpec = function (actorSpec) { 136 class OneFront extends Front {} 137 generateRequestMethods(actorSpec, OneFront.prototype); 138 return OneFront; 139 }; 140 exports.FrontClassWithSpec = FrontClassWithSpec;