MessageHandlerRegistry.sys.mjs (7469B)
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 import { EventEmitter } from "resource://gre/modules/EventEmitter.sys.mjs"; 6 7 const lazy = {}; 8 9 ChromeUtils.defineESModuleGetters(lazy, { 10 Log: "chrome://remote/content/shared/Log.sys.mjs", 11 readSessionData: 12 "chrome://remote/content/shared/messagehandler/sessiondata/SessionDataReader.sys.mjs", 13 RootMessageHandler: 14 "chrome://remote/content/shared/messagehandler/RootMessageHandler.sys.mjs", 15 WindowGlobalMessageHandler: 16 "chrome://remote/content/shared/messagehandler/WindowGlobalMessageHandler.sys.mjs", 17 }); 18 19 ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get()); 20 21 /** 22 * Map of MessageHandler type to MessageHandler subclass. 23 */ 24 ChromeUtils.defineLazyGetter( 25 lazy, 26 "MessageHandlerClasses", 27 () => 28 new Map([ 29 [lazy.RootMessageHandler.type, lazy.RootMessageHandler], 30 [lazy.WindowGlobalMessageHandler.type, lazy.WindowGlobalMessageHandler], 31 ]) 32 ); 33 34 /** 35 * Get the MessageHandler subclass corresponding to the provided type. 36 37 * @param {string} type 38 * MessageHandler type, one of MessageHandler.type. 39 * @returns {Class} 40 * A MessageHandler subclass 41 * @throws {Error} 42 * Throws if no MessageHandler subclass is found for the provided type. 43 */ 44 export function getMessageHandlerClass(type) { 45 if (!lazy.MessageHandlerClasses.has(type)) { 46 throw new Error(`No MessageHandler class available for type "${type}"`); 47 } 48 return lazy.MessageHandlerClasses.get(type); 49 } 50 51 /** 52 * The MessageHandlerRegistry allows to create and retrieve MessageHandler 53 * instances for different session ids. 54 * 55 * A MessageHandlerRegistry instance is bound to a specific MessageHandler type 56 * and context. All MessageHandler instances created by the same registry will 57 * use the type and context of the registry, but each will be associated to a 58 * different session id. 59 * 60 * The registry is useful to retrieve the appropriate MessageHandler instance 61 * after crossing a technical boundary (eg process, thread...). 62 */ 63 export class MessageHandlerRegistry extends EventEmitter { 64 /** 65 * @param {string} type 66 * MessageHandler type, one of MessageHandler.type. 67 * @param {object} context 68 * The context object, which depends on the type. 69 */ 70 constructor(type, context) { 71 super(); 72 73 this._messageHandlerClass = getMessageHandlerClass(type); 74 this._context = context; 75 this._type = type; 76 77 /** 78 * Map of session id to MessageHandler instance 79 */ 80 this._messageHandlersMap = new Map(); 81 82 this._onMessageHandlerDestroyed = 83 this._onMessageHandlerDestroyed.bind(this); 84 this._onMessageHandlerEvent = this._onMessageHandlerEvent.bind(this); 85 } 86 87 /** 88 * Create all message handlers for the current context, based on the content 89 * of the session data. 90 * This should typically be called when the context is ready to be used and 91 * to receive/send commands. 92 */ 93 createAllMessageHandlers() { 94 const data = lazy.readSessionData(); 95 for (const [sessionId, sessionDataItems] of data) { 96 // Create a message handler for this context for each active message 97 // handler session. 98 // TODO: In the future, to support debugging use cases we might want to 99 // only create a message handler if there is relevant data. 100 // For automation scenarios, this is less critical. 101 this._createMessageHandler(sessionId, sessionDataItems); 102 } 103 } 104 105 destroy() { 106 this._messageHandlersMap.forEach(messageHandler => { 107 messageHandler.destroy(); 108 }); 109 } 110 111 /** 112 * Retrieve all MessageHandler instances held in this registry, for all 113 * session IDs. 114 * 115 * @returns {Iterable.<MessageHandler>} 116 * Iterator of MessageHandler instances 117 */ 118 getAllMessageHandlers() { 119 return this._messageHandlersMap.values(); 120 } 121 122 /** 123 * Retrieve an existing MessageHandler instance matching the provided session 124 * id. Returns null if no MessageHandler was found. 125 * 126 * @param {string} sessionId 127 * ID of the session the handler is used for. 128 * @returns {MessageHandler=} 129 * A MessageHandler instance, null if not found. 130 */ 131 getExistingMessageHandler(sessionId) { 132 return this._messageHandlersMap.get(sessionId); 133 } 134 135 /** 136 * Retrieve the MessageHandler instance registered for the provided session 137 * id. Will create and register a MessageHander if no instance was found. 138 * 139 * @param {string} sessionId 140 * ID of the session the handler is used for. 141 * @returns {MessageHandler} 142 * A MessageHandler instance. 143 */ 144 getOrCreateMessageHandler(sessionId) { 145 let messageHandler = this.getExistingMessageHandler(sessionId); 146 if (!messageHandler) { 147 messageHandler = this._createMessageHandler(sessionId); 148 } 149 150 return messageHandler; 151 } 152 153 /** 154 * Retrieve an already registered RootMessageHandler instance matching the 155 * provided sessionId. 156 * 157 * @param {string} sessionId 158 * ID of the session the handler is used for. 159 * @returns {RootMessageHandler} 160 * A RootMessageHandler instance. 161 * @throws {Error} 162 * If no root MessageHandler can be found for the provided session id. 163 */ 164 getRootMessageHandler(sessionId) { 165 const rootMessageHandler = this.getExistingMessageHandler( 166 sessionId, 167 lazy.RootMessageHandler.type 168 ); 169 if (!rootMessageHandler) { 170 throw new Error( 171 `Unable to find a root MessageHandler for session id ${sessionId}` 172 ); 173 } 174 return rootMessageHandler; 175 } 176 177 toString() { 178 return `[object ${this.constructor.name}]`; 179 } 180 181 /** 182 * Create a new MessageHandler instance. 183 * 184 * @param {string} sessionId 185 * ID of the session the handler will be used for. 186 * @param {Array<SessionDataItem>=} sessionDataItems 187 * Optional array of session data items to be applied automatically to the 188 * MessageHandler. 189 * @returns {MessageHandler} 190 * A new MessageHandler instance. 191 */ 192 _createMessageHandler(sessionId, sessionDataItems) { 193 const messageHandler = new this._messageHandlerClass( 194 sessionId, 195 this._context, 196 this 197 ); 198 199 messageHandler.on( 200 "message-handler-destroyed", 201 this._onMessageHandlerDestroyed 202 ); 203 messageHandler.on("message-handler-event", this._onMessageHandlerEvent); 204 205 messageHandler.initialize(sessionDataItems); 206 207 this._messageHandlersMap.set(sessionId, messageHandler); 208 209 lazy.logger.trace( 210 `Created MessageHandler ${this._type} for session ${sessionId}` 211 ); 212 213 return messageHandler; 214 } 215 216 // Event handlers 217 218 _onMessageHandlerDestroyed(eventName, messageHandler) { 219 messageHandler.off( 220 "message-handler-destroyed", 221 this._onMessageHandlerDestroyed 222 ); 223 messageHandler.off("message-handler-event", this._onMessageHandlerEvent); 224 this._messageHandlersMap.delete(messageHandler.sessionId); 225 226 lazy.logger.trace( 227 `Unregistered MessageHandler ${messageHandler.constructor.type} for session ${messageHandler.sessionId}` 228 ); 229 } 230 231 _onMessageHandlerEvent(eventName, messageHandlerEvent) { 232 // The registry simply re-emits MessageHandler events so that consumers 233 // don't have to attach listeners to individual MessageHandler instances. 234 this.emit("message-handler-registry-event", messageHandlerEvent); 235 } 236 }