| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | /* 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/. */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const {protocol, checkScheme} = ChromeUtils.import("chrome://juggler/content/protocol/Protocol.js"); | 
					
						
							|  |  |  | const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const helper = new Helper(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Dispatcher { | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * @param {Connection} connection | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   constructor(connection) { | 
					
						
							|  |  |  |     this._connection = connection; | 
					
						
							|  |  |  |     this._connection.onmessage = this._dispatch.bind(this); | 
					
						
							|  |  |  |     this._connection.onclose = this._dispose.bind(this); | 
					
						
							|  |  |  |     this._sessions = new Map(); | 
					
						
							|  |  |  |     this._rootSession = new ProtocolSession(this, undefined); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   rootSession() { | 
					
						
							|  |  |  |     return this._rootSession; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   createSession() { | 
					
						
							|  |  |  |     const session = new ProtocolSession(this, helper.generateId()); | 
					
						
							|  |  |  |     this._sessions.set(session.sessionId(), session); | 
					
						
							|  |  |  |     return session; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-25 14:50:40 -07:00
										 |  |  |   destroySession(session) { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._sessions.delete(session.sessionId()); | 
					
						
							| 
									
										
										
										
											2020-08-25 14:50:40 -07:00
										 |  |  |     session._dispose(); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _dispose() { | 
					
						
							|  |  |  |     this._connection.onmessage = null; | 
					
						
							|  |  |  |     this._connection.onclose = null; | 
					
						
							| 
									
										
										
										
											2020-10-06 12:06:50 -07:00
										 |  |  |     this._rootSession._dispose(); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._rootSession = null; | 
					
						
							|  |  |  |     this._sessions.clear(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async _dispatch(event) { | 
					
						
							|  |  |  |     const data = JSON.parse(event.data); | 
					
						
							|  |  |  |     const id = data.id; | 
					
						
							|  |  |  |     const sessionId = data.sessionId; | 
					
						
							|  |  |  |     delete data.sessionId; | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       const session = sessionId ? this._sessions.get(sessionId) : this._rootSession; | 
					
						
							|  |  |  |       if (!session) | 
					
						
							|  |  |  |         throw new Error(`ERROR: cannot find session with id "${sessionId}"`); | 
					
						
							|  |  |  |       const method = data.method; | 
					
						
							|  |  |  |       const params = data.params || {}; | 
					
						
							|  |  |  |       if (!id) | 
					
						
							|  |  |  |         throw new Error(`ERROR: every message must have an 'id' parameter`); | 
					
						
							|  |  |  |       if (!method) | 
					
						
							|  |  |  |         throw new Error(`ERROR: every message must have a 'method' parameter`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const [domain, methodName] = method.split('.'); | 
					
						
							|  |  |  |       const descriptor = protocol.domains[domain] ? protocol.domains[domain].methods[methodName] : null; | 
					
						
							|  |  |  |       if (!descriptor) | 
					
						
							|  |  |  |         throw new Error(`ERROR: method '${method}' is not supported`); | 
					
						
							|  |  |  |       let details = {}; | 
					
						
							|  |  |  |       if (!checkScheme(descriptor.params || {}, params, details)) | 
					
						
							|  |  |  |         throw new Error(`ERROR: failed to call method '${method}' with parameters ${JSON.stringify(params, null, 2)}\n${details.error}`); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 00:15:24 -07:00
										 |  |  |       const result = await session.dispatch(method, params); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       details = {}; | 
					
						
							|  |  |  |       if ((descriptor.returns || result) && !checkScheme(descriptor.returns, result, details)) | 
					
						
							|  |  |  |         throw new Error(`ERROR: failed to dispatch method '${method}' result ${JSON.stringify(result, null, 2)}\n${details.error}`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       this._connection.send(JSON.stringify({id, sessionId, result})); | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       this._connection.send(JSON.stringify({id, sessionId, error: { | 
					
						
							|  |  |  |         message: e.message, | 
					
						
							|  |  |  |         data: e.stack | 
					
						
							|  |  |  |       }})); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _emitEvent(sessionId, eventName, params) { | 
					
						
							|  |  |  |     const [domain, eName] = eventName.split('.'); | 
					
						
							|  |  |  |     const scheme = protocol.domains[domain] ? protocol.domains[domain].events[eName] : null; | 
					
						
							|  |  |  |     if (!scheme) | 
					
						
							|  |  |  |       throw new Error(`ERROR: event '${eventName}' is not supported`); | 
					
						
							|  |  |  |     const details = {}; | 
					
						
							|  |  |  |     if (!checkScheme(scheme, params || {}, details)) | 
					
						
							|  |  |  |       throw new Error(`ERROR: failed to emit event '${eventName}' ${JSON.stringify(params, null, 2)}\n${details.error}`); | 
					
						
							|  |  |  |     this._connection.send(JSON.stringify({method: eventName, params, sessionId})); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ProtocolSession { | 
					
						
							|  |  |  |   constructor(dispatcher, sessionId) { | 
					
						
							|  |  |  |     this._sessionId = sessionId; | 
					
						
							|  |  |  |     this._dispatcher = dispatcher; | 
					
						
							| 
									
										
										
										
											2020-10-06 00:15:24 -07:00
										 |  |  |     this._handler = null; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sessionId() { | 
					
						
							|  |  |  |     return this._sessionId; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 00:15:24 -07:00
										 |  |  |   setHandler(handler) { | 
					
						
							|  |  |  |     this._handler = handler; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-25 14:50:40 -07:00
										 |  |  |   _dispose() { | 
					
						
							| 
									
										
										
										
											2020-10-06 00:15:24 -07:00
										 |  |  |     if (this._handler) | 
					
						
							|  |  |  |       this._handler.dispose(); | 
					
						
							|  |  |  |     this._handler = null; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._dispatcher = null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   emitEvent(eventName, params) { | 
					
						
							|  |  |  |     if (!this._dispatcher) | 
					
						
							|  |  |  |       throw new Error(`Session has been disposed.`); | 
					
						
							|  |  |  |     this._dispatcher._emitEvent(this._sessionId, eventName, params); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 00:15:24 -07:00
										 |  |  |   async dispatch(method, params) { | 
					
						
							|  |  |  |     if (!this._handler) | 
					
						
							|  |  |  |       throw new Error(`Session does not have a handler!`); | 
					
						
							|  |  |  |     if (!this._handler[method]) | 
					
						
							|  |  |  |       throw new Error(`Handler for does not implement method "${method}"`); | 
					
						
							|  |  |  |     return await this._handler[method](params); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | this.EXPORTED_SYMBOLS = ['Dispatcher']; | 
					
						
							|  |  |  | this.Dispatcher = Dispatcher; | 
					
						
							|  |  |  | 
 |