UNPKG

appium-remote-debugger

Version:
214 lines 9.46 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const node_events_1 = require("node:events"); const logger_1 = require("../logger"); const lodash_1 = __importDefault(require("lodash")); const support_1 = require("@appium/support"); /** * Handles messages from the Web Inspector and dispatches them as events. * Extends EventEmitter to provide event-based message handling. */ class RpcMessageHandler extends node_events_1.EventEmitter { /** * Handles a message from the Web Inspector by parsing the selector * and emitting appropriate events. * * @param plist - The plist message from the Web Inspector containing * __selector and __argument properties. */ async handleMessage(plist) { const selector = plist.__selector; if (!selector) { logger_1.log.debug('Got an invalid plist'); return; } const argument = plist.__argument; switch (selector) { case '_rpc_reportSetup:': this.emit('_rpc_reportSetup:', null, argument.WIRSimulatorNameKey, argument.WIRSimulatorBuildKey, argument.WIRSimulatorProductVersionKey); break; case '_rpc_reportConnectedApplicationList:': this.emit('_rpc_reportConnectedApplicationList:', null, argument.WIRApplicationDictionaryKey); break; case '_rpc_applicationSentListing:': this.emit('_rpc_forwardGetListing:', null, argument.WIRApplicationIdentifierKey, argument.WIRListingKey); break; case '_rpc_applicationConnected:': this.emit('_rpc_applicationConnected:', null, argument); break; case '_rpc_applicationDisconnected:': this.emit('_rpc_applicationDisconnected:', null, argument); break; case '_rpc_applicationUpdated:': this.emit('_rpc_applicationUpdated:', null, argument); break; case '_rpc_reportConnectedDriverList:': this.emit('_rpc_reportConnectedDriverList:', null, argument); break; case '_rpc_reportCurrentState:': this.emit('_rpc_reportCurrentState:', null, argument); break; case '_rpc_applicationSentData:': await this.handleDataMessage(plist); break; default: logger_1.log.debug(`Debugger got a message for '${selector}' and have no ` + `handler, doing nothing.`); } } /** * Parses the data key from a plist message. * The data key is a JSON string that needs to be parsed. * * @param plist - The plist message containing the data key. * @returns The parsed DataMessage object. * @throws Error if the data key cannot be parsed. */ parseDataKey(plist) { try { return JSON.parse(plist.__argument.WIRMessageDataKey.toString('utf8')); } catch (err) { logger_1.log.error(`Unparseable message data: ${lodash_1.default.truncate(JSON.stringify(plist), { length: 100 })}`); throw new Error(`Unable to parse message data: ${err.message}`); } } /** * Dispatches a data message by emitting events. * If msgId is provided, emits a message-specific event. * Otherwise, emits method-based events with appropriate argument mapping. * * @param msgId - If not empty, emits an event with this ID: <msgId, error, result>. * If empty, emits method-based events: <name, error, ...args>. * @param method - The method name from the data message. * @param params - The parameters from the data message. * @param result - The result from the data message. * @param error - Any error that occurred during message processing. */ async dispatchDataMessage(msgId, method, params, result, error) { if (msgId) { if (this.listenerCount(msgId)) { if (lodash_1.default.has(result?.result, 'value')) { result = result.result.value; } this.emit(msgId, error, result); } else { logger_1.log.error(`Web Inspector returned data for message '${msgId}' ` + `but we were not waiting for that message! ` + `result: '${JSON.stringify(result)}'; ` + `error: '${JSON.stringify(error)}'`); } return; } const eventNames = method ? [method] : []; let args = [params]; // some events have different names, or the arguments are mapped from the // parameters received switch (method) { case 'Page.frameStoppedLoading': eventNames.push('Page.frameNavigated'); // eslint-disable-next-line no-fallthrough case 'Page.frameNavigated': args = [`'${method}' event`]; break; case 'Timeline.eventRecorded': args = [params || params?.record]; break; case 'Console.messageAdded': args = [params?.message]; break; case 'Runtime.executionContextCreated': args = [params?.context]; break; default: // pass break; } if (method && lodash_1.default.startsWith(method, 'Network.')) { // aggregate Network events, and add original method name to the arguments eventNames.push('NetworkEvent'); args.push(method); } if (method && lodash_1.default.startsWith(method, 'Console.')) { // aggregate Console events, and add original method name to the arguments eventNames.push('ConsoleEvent'); args.push(method); } for (const name of eventNames) { this.emit(name, error, ...args); } } /** * Handles a data message from the Web Inspector by parsing it and * dispatching appropriate events based on the message type. * * @param plist - The plist message from the Web Inspector. */ async handleDataMessage(plist) { const dataKey = this.parseDataKey(plist); let msgId = (dataKey.id || '').toString(); let result = dataKey.result; let method = dataKey.method; let params = dataKey.params; const parseError = () => { const defaultMessage = 'Error occurred in handling data message'; if (result?.wasThrown) { const message = result?.result?.value || result?.result?.description ? result?.result?.value || result?.result?.description : (dataKey.error ?? defaultMessage); return new Error(message); } if (dataKey.error) { if (lodash_1.default.isPlainObject(dataKey.error)) { const dataKeyError = dataKey.error; const error = new Error(defaultMessage); for (const key of Object.keys(dataKeyError)) { error[key] = dataKeyError[key]; } return error; } return new Error(String(dataKey.error || defaultMessage)); } return undefined; }; switch (method) { case 'Target.targetCreated': case 'Target.targetDestroyed': case 'Target.didCommitProvisionalTarget': { const app = plist.__argument.WIRApplicationIdentifierKey; const args = method === 'Target.didCommitProvisionalTarget' ? params : (params.targetInfo ?? { targetId: params.targetId }); this.emit(method, null, app, args); return; } case 'Target.dispatchMessageFromTarget': { if (!dataKey.error) { try { const message = JSON.parse(dataKey.params.message); msgId = lodash_1.default.isUndefined(message.id) ? '' : String(message.id); method = message.method; result = message.result || message; params = result.params; } catch (err) { // if this happens then some aspect of the protocol is missing to us // so print the entire message to get visibility into what is going on logger_1.log.error(`Unexpected message format from Web Inspector: ${support_1.util.jsonStringify(plist, null)}`); throw err; } } await this.dispatchDataMessage(msgId, method, params, result, parseError()); return; } default: { await this.dispatchDataMessage(msgId, method, params, result, parseError()); } } } } exports.default = RpcMessageHandler; //# sourceMappingURL=rpc-message-handler.js.map