diff options
Diffstat (limited to 'devtools/shared/protocol/Front/FrontClassWithSpec.js')
-rw-r--r-- | devtools/shared/protocol/Front/FrontClassWithSpec.js | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/devtools/shared/protocol/Front/FrontClassWithSpec.js b/devtools/shared/protocol/Front/FrontClassWithSpec.js new file mode 100644 index 0000000000..55091be3e4 --- /dev/null +++ b/devtools/shared/protocol/Front/FrontClassWithSpec.js @@ -0,0 +1,118 @@ +/* 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 { Front } = require("resource://devtools/shared/protocol/Front.js"); + +/** + * Generates request methods as described by the given actor specification on + * the given front prototype. Returns the front prototype. + */ +var generateRequestMethods = function (actorSpec, frontProto) { + if (frontProto._actorSpec) { + throw new Error("frontProto called twice on the same front prototype!"); + } + + frontProto.typeName = actorSpec.typeName; + + // Generate request methods. + const methods = actorSpec.methods; + methods.forEach(spec => { + const name = spec.name; + + frontProto[name] = function (...args) { + // If the front is destroyed, the request will not be able to complete. + if (this.isDestroyed()) { + throw new Error( + `Can not send request '${name}' because front '${this.typeName}' is already destroyed.` + ); + } + + const startTime = Cu.now(); + let packet; + try { + packet = spec.request.write(args, this); + } catch (ex) { + console.error("Error writing request: " + name); + throw ex; + } + if (spec.oneway) { + // Fire-and-forget oneway packets. + this.send(packet); + return undefined; + } + + return this.request(packet).then(response => { + let ret; + if (!this.conn) { + throw new Error("Missing conn on " + this); + } + if (this.isDestroyed()) { + throw new Error( + `Can not interpret '${name}' response because front '${this.typeName}' is already destroyed.` + ); + } + try { + ret = spec.response.read(response, this); + } catch (ex) { + console.error("Error reading response to: " + name + "\n" + ex); + throw ex; + } + ChromeUtils.addProfilerMarker( + "RDP Front", + startTime, + `${this.typeName}:${name}()` + ); + return ret; + }); + }; + + // Release methods should call the destroy function on return. + if (spec.release) { + const fn = frontProto[name]; + frontProto[name] = function (...args) { + return fn.apply(this, args).then(result => { + this.destroy(); + return result; + }); + }; + } + }); + + // Process event specifications + frontProto._clientSpec = {}; + + const actorEvents = actorSpec.events; + if (actorEvents) { + frontProto._clientSpec.events = new Map(); + + for (const [name, request] of actorEvents) { + frontProto._clientSpec.events.set(request.type, { + name, + request, + }); + } + } + + frontProto._actorSpec = actorSpec; + + return frontProto; +}; + +/** + * Create a front class for the given actor specification and front prototype. + * + * @param object actorSpec + * The actor specification you're creating a front for. + * @param object proto + * The object prototype. Must have a 'typeName' property, + * should have method definitions, can have event definitions. + */ +var FrontClassWithSpec = function (actorSpec) { + class OneFront extends Front {} + generateRequestMethods(actorSpec, OneFront.prototype); + return OneFront; +}; +exports.FrontClassWithSpec = FrontClassWithSpec; |