UNPKG

jsms-ext-chromium

Version:

JSMS extension for integration of Chromium Embedded Framework (CEF) clients.

252 lines (247 loc) 11.9 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('jsms'), require('@log4js-node/log4js-api')) : typeof define === 'function' && define.amd ? define(['exports', 'jsms', '@log4js-node/log4js-api'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.jsms_ext_chromium = {}, global.jsms, global.log4jsApi)); })(this, (function (exports, jsms, log4jsApi) { 'use strict'; function currentTimeMillis() { return new Date().getTime(); } class ChromiumConnection extends jsms.JsmsConnection { /** * @param defaultTimeToLive will be used to calculate expiration time when no * custom value is provided to the send function * @param maxHandshakeRetries specifies how often the handshake will be retried * @param globalNS is only used by unit tests - * in production code you should ignore it and just leave it undefined */ constructor(defaultTimeToLive = ChromiumConnection.DEFAULT_TIME_TO_LIVE, maxHandshakeRetries = ChromiumConnection.DEFAULT_HANDSHAKE_RETRY_COUNT, globalNS = window) { super(); this.defaultTimeToLive = defaultTimeToLive; this.maxHandshakeRetries = maxHandshakeRetries; this.globalNS = globalNS; this.logger = log4jsApi.getLogger("[CHROMIUM]"); this.sendFunction = null; this.responseDeferreds = new Map(); this.currentHandshakeRetries = 0; this.globalNS.onMessage = (json) => { try { this.onMessage(jsms.JsmsMessage.fromJSON(json)); } catch (e) { this.logger.error(e); } }; } onMessage(message) { const responseDeferred = this.responseDeferreds.get(message.header.correlationID); if (responseDeferred) { this.handleResponse(message, responseDeferred); } else { const destination = this.getDestinationFor(message.header.destination); const consumer = this.getConsumer(destination); consumer.onMessage(message).then(response => { if (Object.keys(response.body).length > 0) { this.send(response); } }); } } handleResponse(response, responseDeferred) { if (this.logger.isDebugEnabled()) { this.logger.debug("Receiving response: \"" + response.header.destination + "\" [" + response.header.correlationID + "]:\n" + JSON.stringify(response.body)); } this.responseDeferreds.delete(response.header.correlationID); responseDeferred.resolve(response); } createQueue(queueName) { const queue = new jsms.JsmsQueue(queueName); this.addQueue(queue, new jsms.JsmsQueueSender(this, queue), new jsms.JsmsQueueReceiver(queue)); return queue; } createTopic(topicName) { const topic = new jsms.JsmsTopic(topicName); this.addTopic(topic, new jsms.JsmsTopicPublisher(topic), new jsms.JsmsTopicSubscriber(topic)); return topic; } send(message) { if (this.logger.isDebugEnabled()) { this.logger.debug("Sending request: \"" + message.header.destination + "\" [" + message.header.correlationID + "]:\n" + JSON.stringify(message.body)); } const deferredResponse = new jsms.JsmsDeferred(); const sendFunction = this.getSendFunction(); sendFunction(message, deferredResponse); this.handleExpiration(message, deferredResponse); return deferredResponse; } getSendFunction() { if (this.sendFunction) { return this.sendFunction; } if (this.isCEF()) { this.initCEFConnection(); } else if (this.isWebView2()) { this.initWebView2Connection(); } else { throw new Error("No messaging function available"); } return this.sendFunction; } isCEF() { return typeof this.globalNS.cefQuery === "function"; } initCEFConnection() { this.sendFunction = (message, deferredResponse) => this.sendToCEF(message, deferredResponse); } isWebView2() { return this.globalNS.chrome && this.globalNS.chrome.webview && typeof this.globalNS.chrome.webview.postMessage === "function"; } initWebView2Connection() { this.sendFunction = (message, deferredResponse) => this.sendToWebView2(message, deferredResponse); this.globalNS.chrome.webview.addEventListener('message', (arg) => { const envelope = arg.data; const status = envelope.status; if (status === 0) { const response = jsms.JsmsMessage.fromJSON(envelope.response); this.handleSuccessResponse(response); } else { const request = jsms.JsmsMessage.fromJSON(envelope.request); this.handleErrorResponse(request.header.destination, request.header.correlationID, envelope.errorCode, envelope.errorMessage); } }); } handleSuccessResponse(response) { const responseDeferred = this.responseDeferreds.get(response.header.correlationID); responseDeferred === null || responseDeferred === void 0 ? void 0 : responseDeferred.resolve(response); this.responseDeferreds.delete(response.header.correlationID); } handleErrorResponse(destination, correlationID, errorCode, errorMessage) { this.logger.error("Native call failed for: " + "\ndestination: " + destination + "\nerror-code: " + errorCode + "\nerror-message: " + errorMessage); const responseDeferred = this.responseDeferreds.get(correlationID); responseDeferred === null || responseDeferred === void 0 ? void 0 : responseDeferred.reject(errorMessage); this.responseDeferreds.delete(correlationID); } sendToCEF(message, deferredResponse) { this.responseDeferreds.set(message.header.correlationID, deferredResponse); const cefQuery = { request: message.toString(), persistent: false, onSuccess: (responseString) => { this.handleSuccessResponse(jsms.JsmsMessage.fromString(responseString)); }, onFailure: (errorCode, errorMessage) => { this.handleErrorResponse(message.header.destination, message.header.correlationID, errorCode, errorMessage); } }; this.globalNS.cefQuery(cefQuery); } sendToWebView2(message, deferredResponse) { this.responseDeferreds.set(message.header.correlationID, deferredResponse); this.globalNS.chrome.webview.postMessage(message.toString()); } handleExpiration(message, deferredResponse) { if (message.header.expiration === 0) { return; } let timeToLive = message.header.expiration - currentTimeMillis(); timeToLive = Math.max(0, timeToLive); setTimeout(() => { if (this.responseDeferreds.has(message.header.correlationID)) { this.responseDeferreds.delete(message.header.correlationID); deferredResponse.reject(message.createExpirationMessage()); } }, timeToLive); } sendHandshake() { this.currentHandshakeRetries = 0; const outerDeferred = new jsms.JsmsDeferred(); try { this.sendHandshakeInternal(outerDeferred); } catch (e) { outerDeferred.reject(e); } return outerDeferred; } sendHandshakeInternal(outerDeferred) { this.logger.info("Beginning HANDSHAKE @" + currentTimeMillis() + "..."); // first pass: check if client receives messages const deferred = this.sendHandshakeInit(); deferred.then(() => { // second pass: let the client know that we are able to receive messages this.sendServerReady() .then(ack => { // client acknowledged - connection successfully established this.resolveHandshake(outerDeferred, ack); }) .catch(reason => { this.retryHandshake(outerDeferred, reason); }); }) .catch(error => { this.retryHandshake(outerDeferred, error); }); return deferred; } sendHandshakeInit() { const initMessage = jsms.JsmsMessage.create(ChromiumConnection.HANDSHAKE_INIT, {}, this.defaultTimeToLive); const deferred = this.send(initMessage); deferred.catch(() => { this.logger.error("HANDSHAKE INIT TIMEOUT: " + initMessage.header.correlationID + ' @' + currentTimeMillis()); }); return deferred; } sendServerReady() { const serverReadyMessage = jsms.JsmsMessage.create(ChromiumConnection.HANDSHAKE_SERVER_READY, {}, this.defaultTimeToLive); const deferred = this.send(serverReadyMessage); deferred.catch(() => { this.logger.error("SERVER READY TIMEOUT: " + serverReadyMessage.header.correlationID + ' @' + currentTimeMillis()); }); return deferred; } resolveHandshake(outerDeferred, ack) { this.logger.info("HANDSHAKE SUCCESSFUL @" + currentTimeMillis()); outerDeferred.resolve(ack); } retryHandshake(outerDeferred, error) { if (this.currentHandshakeRetries++ < this.maxHandshakeRetries) { this.logger.error("HANDSHAKE FAILED, retrying ..."); setTimeout(() => { this.sendHandshakeInternal(outerDeferred); }, ChromiumConnection.DEFAULT_HANDSHAKE_RETRY_DELAY); } else { this.logger.error("HANDSHAKE FAILED @" + currentTimeMillis()); outerDeferred.reject(error); } } } ChromiumConnection.HANDSHAKE_INIT = "/jsms-ext-chromium/handshake/init"; ChromiumConnection.HANDSHAKE_CLIENT_READY = "/jsms-ext-chromium/handshake/client/ready"; ChromiumConnection.HANDSHAKE_SERVER_READY = "/jsms-ext-chromium/handshake/server/ready"; ChromiumConnection.DEFAULT_TIME_TO_LIVE = 1000; ChromiumConnection.DEFAULT_HANDSHAKE_RETRY_COUNT = 60; ChromiumConnection.DEFAULT_HANDSHAKE_RETRY_DELAY = 100; exports.ChromiumConnection = ChromiumConnection; Object.defineProperty(exports, '__esModule', { value: true }); }));