PageAgent.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // node-inspector version of on webkit-inspector/InspectorPageAgent.cpp
  2. var fs = require('fs'),
  3. path = require('path'),
  4. inherits = require('util').inherits,
  5. extend = require('util')._extend,
  6. EventEmitter = require('events').EventEmitter,
  7. async = require('async'),
  8. convert = require('./convert.js'),
  9. ScriptFileStorage = require('./ScriptFileStorage.js').ScriptFileStorage;
  10. /**
  11. * @param {{preload}} config
  12. * @param {DebuggerClient} debuggerClient
  13. * @param {ScriptManager} scriptManager
  14. * @constructor
  15. */
  16. function PageAgent(config, debuggerClient, scriptManager) {
  17. this._debuggerClient = debuggerClient;
  18. this._scriptManager = scriptManager;
  19. this._scriptStorage = new ScriptFileStorage(config, scriptManager);
  20. }
  21. inherits(PageAgent, EventEmitter);
  22. extend(PageAgent.prototype, {
  23. enable: function(params, done) {
  24. done();
  25. },
  26. canShowFPSCounter: function(params, done) {
  27. done(null, { show: false });
  28. },
  29. canContinuouslyPaint: function(params, done) {
  30. done(null, { value: false });
  31. },
  32. setTouchEmulationEnabled: function(params, done) {
  33. done();
  34. },
  35. getResourceTree: function(params, done) {
  36. var cb = function() {
  37. done.apply(null, arguments);
  38. this.emit('resource-tree');
  39. }.bind(this);
  40. if (this._debuggerClient.isConnected) {
  41. this._doGetResourceTree(params, cb);
  42. } else {
  43. this._debuggerClient.once(
  44. 'connect',
  45. this._doGetResourceTree.bind(this, params, cb)
  46. );
  47. }
  48. },
  49. _doGetResourceTree: function(params, done) {
  50. var describeProgram = '[process.cwd(), ' +
  51. 'process.mainModule ? process.mainModule.filename : process.argv[1]]';
  52. async.waterfall(
  53. [
  54. this._debuggerClient.evaluateGlobal
  55. .bind(this._debuggerClient, describeProgram),
  56. function(evaluateResult, cb) {
  57. cb(null, evaluateResult[0], evaluateResult[1]);
  58. },
  59. this._resolveMainAppScript.bind(this),
  60. this._getResourceTreeForAppScript.bind(this)
  61. ],
  62. done
  63. );
  64. },
  65. _resolveMainAppScript: function(startDirectory, mainAppScript, done) {
  66. this._scriptManager.mainAppScript = mainAppScript;
  67. if (mainAppScript == null) {
  68. // mainScriptFile is null when running in the REPL mode
  69. return done(null, startDirectory, mainAppScript);
  70. }
  71. fs.stat(mainAppScript, function(err, stat) {
  72. if (err && !/\.js$/.test(mainAppScript)) {
  73. mainAppScript += '.js';
  74. }
  75. return done(null, startDirectory, mainAppScript);
  76. });
  77. },
  78. _getResourceTreeForAppScript: function(startDirectory, mainAppScript, done) {
  79. async.waterfall(
  80. [
  81. this._scriptStorage.findAllApplicationScripts
  82. .bind(this._scriptStorage, startDirectory, mainAppScript),
  83. this._createResourceTreeResponse.bind(this, mainAppScript)
  84. ],
  85. done
  86. );
  87. },
  88. _createResourceTreeResponse: function(mainAppScript, scriptFiles, done) {
  89. var resources = scriptFiles.map(function(filePath) {
  90. return {
  91. url: convert.v8NameToInspectorUrl(filePath),
  92. type: 'Script',
  93. mimeType: 'text/javascript'
  94. };
  95. });
  96. done(null, {
  97. frameTree: {
  98. frame: {
  99. id: 'nodeinspector-toplevel-frame',
  100. url: convert.v8NameToInspectorUrl(mainAppScript),
  101. // Front-end keeps a history of local modifications based
  102. // on loaderId. Ideally we should return such id that it remains
  103. // same as long as the the debugger process has the same content
  104. // of scripts and that changes when a new content is loaded.
  105. //
  106. // To keep things easy, we are returning an unique value for now.
  107. // This means that every reload of node-inspector page discards
  108. // the history of live-edit changes.
  109. //
  110. // Perhaps we can use PID as loaderId instead?
  111. loaderId: createUniqueLoaderId(),
  112. _isNodeInspectorScript: true
  113. },
  114. resources: resources
  115. }
  116. });
  117. },
  118. getResourceContent: function(params, done) {
  119. var scriptName = convert.inspectorUrlToV8Name(params.url);
  120. if (scriptName === '') {
  121. // When running REPL, main application file is null
  122. // and node inspector returns an empty string to the front-end.
  123. // However, front-end still asks for resource content.
  124. // Let's return a descriptive comment then.
  125. var content = '// There is no main module loaded in node.\n' +
  126. '// This is expected when you are debugging node\'s interactive REPL console.';
  127. return process.nextTick(
  128. this._convertScriptSourceToGetResourceResponse.bind(this, content, done));
  129. }
  130. async.waterfall(
  131. [
  132. this._scriptStorage.load.bind(this._scriptStorage, scriptName),
  133. this._convertScriptSourceToGetResourceResponse.bind(this)
  134. ],
  135. done
  136. );
  137. },
  138. _convertScriptSourceToGetResourceResponse: function(source, done) {
  139. return done(null, {
  140. content: source
  141. });
  142. },
  143. reload: function(params, done) {
  144. // This is called when user press Cmd+R (F5?), do we want to perform an action on this?
  145. done();
  146. }
  147. });
  148. exports.PageAgent = PageAgent;
  149. function createUniqueLoaderId() {
  150. var randomPart = String(Math.random()).slice(2);
  151. return Date.now() + '-' + randomPart;
  152. }