var convert = require('./convert'), CallFramesProvider = require('./CallFramesProvider').CallFramesProvider; /** * @param {Object} config * @param {FrontendClient} frontendClient * @param {DebuggerClient} debuggerClient * @param {ScriptManager} scriptManager * @constructor */ function BreakEventHandler(config, frontendClient, debuggerClient, scriptManager) { this._config = config; this._frontendClient = frontendClient; this._debuggerClient = debuggerClient; this._scriptManager = scriptManager; this._callFramesProvider = new CallFramesProvider(config, debuggerClient); this._registerDebuggerEventHandlers(); } var callbackForNextBreak; Object.defineProperties(BreakEventHandler.prototype, { /** @type {Number} */ continueToLocationBreakpointId: { writable: true, value: null }, /** @type {function(eventData)} */ callbackForNextBreak: { get: function() { return callbackForNextBreak; }, set: function(value) { if (value && callbackForNextBreak) throw new Error('Cannot set multiple callbacks for the next break.'); callbackForNextBreak = value; } } }); BreakEventHandler.prototype._registerDebuggerEventHandlers = function() { this._debuggerClient.on('break', this._onBreak.bind(this)); this._debuggerClient.on('exception', this._onBreak.bind(this)); }; BreakEventHandler.prototype._onBreak = function(obj) { var scriptId = obj.script.id, hitBreakpoints = obj.breakpoints, source = this._scriptManager.findScriptByID(scriptId); // Source is undefined when the breakpoint was in code eval()-ed via // console or eval()-ed internally by node inspector. // We could send backtrace in such case, but that isn't working well now. // V8 is reusing the same scriptId for multiple eval() calls and DevTools // front-end does not update the displayed source code when a content // of a script changes. // The following solution - ignore the breakpoint and resume the // execution - should be good enough in most cases. if (!source || source.hidden) { this._debuggerClient.request('continue', { stepaction: 'out' }); return; } if (this.callbackForNextBreak) { var callback = this.callbackForNextBreak; this.callbackForNextBreak = null; callback(obj); return; } if (this.continueToLocationBreakpointId !== null) { this._debuggerClient.clearBreakpoint( this.continueToLocationBreakpointId, function(err, result) { if (err) this._frontendClient.sendLogToConsole('warning', err); else this.continueToLocationBreakpointId = null; }.bind(this) ); } this.sendBacktraceToFrontend(obj.exception, hitBreakpoints); }; /** * @param {function(error, response)} callback */ BreakEventHandler.prototype.fetchCallFrames = function(callback) { this._callFramesProvider.fetchCallFrames(callback); }; /** * @param {Object} exception * @param {Array.} hitBreakpoints */ BreakEventHandler.prototype.sendBacktraceToFrontend = function(exception, hitBreakpoints) { this.fetchCallFrames(function(error, result) { if (error) this._frontendClient.sendLogToConsole('error', error); else this._frontendClient.sendEvent( 'Debugger.paused', { callFrames: result, reason: exception ? 'exception' : 'other', data: exception ? convert.v8RefToInspectorObject(exception) : null, hitBreakpoints: hitBreakpoints }); }.bind(this)); }; exports.BreakEventHandler = BreakEventHandler;