playwright/browser_patches/firefox/juggler/protocol/Dispatcher.js

136 lines
4.3 KiB
JavaScript

/* 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;
}
destroySession(session) {
this._sessions.delete(session.sessionId());
session._dispose();
}
_dispose() {
this._connection.onmessage = null;
this._connection.onclose = null;
this._rootSession._dispose();
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}`);
const result = await session.dispatch(method, params);
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;
this._handler = null;
}
sessionId() {
return this._sessionId;
}
setHandler(handler) {
this._handler = handler;
}
_dispose() {
if (this._handler)
this._handler.dispose();
this._handler = null;
this._dispatcher = null;
}
emitEvent(eventName, params) {
if (!this._dispatcher)
throw new Error(`Session has been disposed.`);
this._dispatcher._emitEvent(this._sessionId, eventName, params);
}
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);
}
}
this.EXPORTED_SYMBOLS = ['Dispatcher'];
this.Dispatcher = Dispatcher;