Transport.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.WindowCdpTransport = exports.WindowBidiTransport = void 0;
  4. const log_js_1 = require("../utils/log.js");
  5. const mapperTabPage_js_1 = require("./mapperTabPage.js");
  6. class WindowBidiTransport {
  7. static LOGGER_PREFIX_RECV = `${log_js_1.LogType.bidi}:RECV ◂`;
  8. static LOGGER_PREFIX_SEND = `${log_js_1.LogType.bidi}:SEND ▸`;
  9. #onMessage = null;
  10. constructor() {
  11. window.onBidiMessage = (message) => {
  12. (0, mapperTabPage_js_1.log)(WindowBidiTransport.LOGGER_PREFIX_RECV, message);
  13. try {
  14. const command = WindowBidiTransport.#parseBidiMessage(message);
  15. this.#onMessage?.call(null, command);
  16. }
  17. catch (e) {
  18. const error = e instanceof Error ? e : new Error(e);
  19. // Transport-level error does not provide channel.
  20. this.#respondWithError(message, "invalid argument" /* ErrorCode.InvalidArgument */, error, null);
  21. }
  22. };
  23. }
  24. setOnMessage(onMessage) {
  25. this.#onMessage = onMessage;
  26. }
  27. sendMessage(message) {
  28. (0, mapperTabPage_js_1.log)(WindowBidiTransport.LOGGER_PREFIX_SEND, message);
  29. const json = JSON.stringify(message);
  30. window.sendBidiResponse(json);
  31. }
  32. close() {
  33. this.#onMessage = null;
  34. window.onBidiMessage = null;
  35. }
  36. #respondWithError(plainCommandData, errorCode, error, channel) {
  37. const errorResponse = WindowBidiTransport.#getErrorResponse(plainCommandData, errorCode, error);
  38. if (channel) {
  39. this.sendMessage({
  40. ...errorResponse,
  41. channel,
  42. });
  43. }
  44. else {
  45. this.sendMessage(errorResponse);
  46. }
  47. }
  48. static #getJsonType(value) {
  49. if (value === null) {
  50. return 'null';
  51. }
  52. if (Array.isArray(value)) {
  53. return 'array';
  54. }
  55. return typeof value;
  56. }
  57. static #getErrorResponse(message, errorCode, error) {
  58. // XXX: this is bizarre per spec. We reparse the payload and
  59. // extract the ID, regardless of what kind of value it was.
  60. let messageId;
  61. try {
  62. const command = JSON.parse(message);
  63. if (WindowBidiTransport.#getJsonType(command) === 'object' &&
  64. 'id' in command) {
  65. messageId = command.id;
  66. }
  67. }
  68. catch { }
  69. return {
  70. type: 'error',
  71. id: messageId,
  72. error: errorCode,
  73. message: error.message,
  74. };
  75. }
  76. static #parseBidiMessage(message) {
  77. let command;
  78. try {
  79. command = JSON.parse(message);
  80. }
  81. catch {
  82. throw new Error('Cannot parse data as JSON');
  83. }
  84. const type = WindowBidiTransport.#getJsonType(command);
  85. if (type !== 'object') {
  86. throw new Error(`Expected JSON object but got ${type}`);
  87. }
  88. // Extract and validate id, method and params.
  89. const { id, method, params } = command;
  90. const idType = WindowBidiTransport.#getJsonType(id);
  91. if (idType !== 'number' || !Number.isInteger(id) || id < 0) {
  92. // TODO: should uint64_t be the upper limit?
  93. // https://tools.ietf.org/html/rfc7049#section-2.1
  94. throw new Error(`Expected unsigned integer but got ${idType}`);
  95. }
  96. const methodType = WindowBidiTransport.#getJsonType(method);
  97. if (methodType !== 'string') {
  98. throw new Error(`Expected string method but got ${methodType}`);
  99. }
  100. const paramsType = WindowBidiTransport.#getJsonType(params);
  101. if (paramsType !== 'object') {
  102. throw new Error(`Expected object params but got ${paramsType}`);
  103. }
  104. let channel = command.channel;
  105. if (channel !== undefined) {
  106. const channelType = WindowBidiTransport.#getJsonType(channel);
  107. if (channelType !== 'string') {
  108. throw new Error(`Expected string channel but got ${channelType}`);
  109. }
  110. // Empty string channel is considered as no channel provided.
  111. if (channel === '') {
  112. channel = undefined;
  113. }
  114. }
  115. return { id, method, params, channel };
  116. }
  117. }
  118. exports.WindowBidiTransport = WindowBidiTransport;
  119. class WindowCdpTransport {
  120. #onMessage = null;
  121. constructor() {
  122. window.cdp.onmessage = (message) => {
  123. this.#onMessage?.call(null, message);
  124. };
  125. }
  126. setOnMessage(onMessage) {
  127. this.#onMessage = onMessage;
  128. }
  129. sendMessage(message) {
  130. window.cdp.send(message);
  131. }
  132. close() {
  133. this.#onMessage = null;
  134. window.cdp.onmessage = null;
  135. }
  136. }
  137. exports.WindowCdpTransport = WindowCdpTransport;
  138. //# sourceMappingURL=Transport.js.map