FrontendClient.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. var EventEmitter = require('events').EventEmitter,
  2. inherits = require('util').inherits,
  3. debugProtocol = require('debug')('node-inspector:protocol:devtools'),
  4. ErrorNotConnected = require('./DebuggerClient').ErrorNotConnected;
  5. /**
  6. * FrontendClient encapsulates communication with front-end running in browser.
  7. * @param {{on: Function, send: Function}} connection
  8. * Socket.io connection object.
  9. * @constructor
  10. * @extends EventEmitter
  11. */
  12. function FrontendClient(connection) {
  13. this._connection = connection;
  14. this._registerEventHandlers();
  15. }
  16. inherits(FrontendClient, EventEmitter);
  17. Object.defineProperties(FrontendClient.prototype, {
  18. /** @type {boolean} */
  19. isConnected: {
  20. get: function() {
  21. return this._connection != null;
  22. }
  23. }
  24. });
  25. FrontendClient.prototype._registerEventHandlers = function() {
  26. this._connection.on('close', this._onConnectionClose.bind(this));
  27. this._connection.on('message', this._onConnectionMessage.bind(this));
  28. };
  29. FrontendClient.prototype._onConnectionClose = function() {
  30. this._connection = null;
  31. this.emit('close');
  32. };
  33. FrontendClient.prototype._onConnectionMessage = function(message) {
  34. this.emit('message', message);
  35. };
  36. /**
  37. * Send a message to front-end.
  38. * @param {!Object|string} message
  39. */
  40. FrontendClient.prototype._sendMessage = function(message) {
  41. var payload = typeof message == 'string' ? message : JSON.stringify(message);
  42. debugProtocol('backend: ' + payload);
  43. if (!this._connection) {
  44. if (!this._errorMessageDisplayed) {
  45. console.log('Cannot send response - there is no front-end connection.');
  46. this._errorMessageDisplayed = true;
  47. }
  48. return;
  49. }
  50. this._errorMessageDisplayed = false;
  51. this._connection.send(payload);
  52. };
  53. /**
  54. * Send a response to a front-end request.
  55. * @param {number} requestId Id of the request.
  56. * @param {string} fullMethodName
  57. * @param {?string} error Error message or null/undefined on success.
  58. * @param {Object=} result Response data on success.
  59. */
  60. FrontendClient.prototype.sendResponse = function(requestId,
  61. fullMethodName,
  62. error,
  63. result) {
  64. if (requestId == null) {
  65. throw new Error(
  66. 'Cannot send response to ' +
  67. fullMethodName +
  68. ' without a requestId');
  69. }
  70. var message = { id: requestId };
  71. if (error != null) {
  72. this._onErrorResponse(fullMethodName, error);
  73. message.error = error.toString();
  74. } else {
  75. message.result = result;
  76. }
  77. this._sendMessage(message);
  78. };
  79. FrontendClient.prototype._onErrorResponse = function(fullMethodName, error) {
  80. if (error instanceof ErrorNotConnected) {
  81. this.sendInspectorDetached(error.message);
  82. }
  83. this.sendLogToConsole('error', fullMethodName + ' failed.\n' + error);
  84. };
  85. /**
  86. * Send an event to the front-end.
  87. * @param {string} eventName Event name in form 'Agent.method'.
  88. * @param {Object=} data Event data (method arguments).
  89. */
  90. FrontendClient.prototype.sendEvent = function(eventName, data) {
  91. var message = {
  92. method: eventName,
  93. params: data || {}
  94. };
  95. if (this._eventsPaused) {
  96. this._eventsBuffer.push(message);
  97. } else {
  98. this._sendMessage(message);
  99. }
  100. };
  101. FrontendClient.prototype.pauseEvents = function() {
  102. if (this._eventsPaused) return;
  103. this._eventsPaused = true;
  104. this._eventsBuffer = [];
  105. };
  106. FrontendClient.prototype.resumeEvents = function() {
  107. if (!this._eventsPaused) return;
  108. // We are making a copy of the buffered messages list,
  109. // so that the messages are sent event when pauseEvents()
  110. // is called before the next tick.
  111. var messages = this._eventsBuffer;
  112. process.nextTick(function() {
  113. messages.forEach(this._sendMessage, this);
  114. }.bind(this));
  115. this._eventsPaused = false;
  116. this._eventsBuffer = null;
  117. };
  118. /**
  119. * Ask frontend to add a new log into console window.
  120. * @param {string} level Message level (error, warning, log, debug).
  121. * @param {string} text
  122. */
  123. FrontendClient.prototype.sendLogToConsole = function(level, text) {
  124. this._sendMessage('showConsole');
  125. this.sendEvent(
  126. 'Console.messageAdded',
  127. {
  128. message: {
  129. source: 3,
  130. type: 0,
  131. level: level,
  132. line: 0,
  133. column: 0,
  134. url: '',
  135. groupLevel: 7,
  136. repeatCount: 1,
  137. text: text
  138. }
  139. }
  140. );
  141. };
  142. /**
  143. * Shortcut for sendEvent('Inspector.detached', reason)
  144. * @param {string} reason
  145. */
  146. FrontendClient.prototype.sendInspectorDetached = function(reason) {
  147. this.sendEvent('Inspector.detached', { reason: reason });
  148. };
  149. exports.FrontendClient = FrontendClient;