From 66e8f2e6e53a03c6e526e60eb85bb96894d8e783 Mon Sep 17 00:00:00 2001 From: "yurys@google.com" Date: Tue, 31 Mar 2009 13:10:31 +0000 Subject: 1. Allow to add breakpoints using Scripts panel. 2. Support step in/step out/step over/resume actions in Scripts panel. Review URL: http://codereview.chromium.org/56108 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@12855 0039d316-1c4b-4281-b951-d872f2087c98 --- webkit/glue/devtools/js/debugger_agent.js | 305 ++++++++++++++++++++- .../glue/devtools/js/inspector_controller_impl.js | 74 ++--- webkit/glue/webdevtoolsclient_impl.cc | 9 + 3 files changed, 353 insertions(+), 35 deletions(-) diff --git a/webkit/glue/devtools/js/debugger_agent.js b/webkit/glue/devtools/js/debugger_agent.js index abc774b..820393c 100644 --- a/webkit/glue/devtools/js/debugger_agent.js +++ b/webkit/glue/devtools/js/debugger_agent.js @@ -15,6 +15,28 @@ goog.provide('devtools.DebuggerAgent'); devtools.DebuggerAgent = function() { RemoteDebuggerAgent.DebuggerOutput = goog.bind(this.handleDebuggerOutput_, this); + + /** + * Mapping from script id to script info. + * @type {Object} + */ + this.parsedScripts_ = {}; + + /** + * Mapping from the request id to the devtools.BreakpointInfo for the + * breakpoints whose v8 ids are not set yet. These breakpoints are waiting for + * 'setbreakpoint' responses to learn their ids in the v8 debugger. + * @see #handleSetBreakpointResponse_ + * @type {!Object} + */ + this.requestNumberToBreakpointInfo_ = {}; + + /** + * Information on current stack top frame. + * See JavaScriptCallFrame.idl. + * @type {?Object} + */ + this.currentCallFrame_ = null; }; @@ -26,13 +48,118 @@ devtools.DebuggerAgent.prototype.requestScripts = function() { var cmd = new devtools.DebugCommand('scripts', { 'includeSource': true }); - RemoteDebuggerCommandExecutor.DebuggerCommand(cmd.toJSONProtocol()); + devtools.DebuggerAgent.sendCommand_(cmd); // Force v8 execution so that it gets to processing the requested command. devtools.tools.evaluateJavaSctipt('javascript:void(0)'); }; /** + * @param {number} sourceId Id of the script fot the breakpoint. + * @param {number} line Number of the line for the breakpoint. + */ +devtools.DebuggerAgent.prototype.addBreakpoint = function(sourceId, line) { + var script = this.parsedScripts_[sourceId]; + if (!script) { + return; + } + + var breakpointInfo = script.getBreakpointInfo(line); + if (breakpointInfo) { + return; + } + + breakpointInfo = new devtools.BreakpointInfo(sourceId, line); + script.addBreakpointInfo(breakpointInfo); + + line += script.getLineOffset() - 1; + var cmd = new devtools.DebugCommand('setbreakpoint', { + 'type': 'scriptId', + 'target': sourceId, + 'line': line + }); + + this.requestNumberToBreakpointInfo_[cmd.getSequenceNumber()] = breakpointInfo; + + devtools.DebuggerAgent.sendCommand_(cmd); +}; + + +/** + * Tells the v8 debugger to step into the next statement. + */ +devtools.DebuggerAgent.prototype.stepIntoStatement = function() { + this.stepCommand_('in'); +}; + + +/** + * Tells the v8 debugger to step out of current function. + */ +devtools.DebuggerAgent.prototype.stepOutOfFunction = function() { + this.stepCommand_('out'); +}; + + +/** + * Tells the v8 debugger to step over the next statement. + */ +devtools.DebuggerAgent.prototype.stepOverStatement = function() { + this.stepCommand_('next'); +}; + + +/** + * Tells the v8 debugger to continue execution after it has been stopped on a + * breakpoint or an exception. + */ +devtools.DebuggerAgent.prototype.continueExecution = function() { + var cmd = new devtools.DebugCommand('continue'); + devtools.DebuggerAgent.sendCommand_(cmd); +}; + + +/** + * Current stack top frame. + * @return {Object} + */ +devtools.DebuggerAgent.prototype.getCurrentCallFrame = function() { + return this.currentCallFrame_; +}; + + +/** + * Sends 'backtrace' request to v8. + */ +devtools.DebuggerAgent.prototype.requestBacktrace_ = function() { + var cmd = new devtools.DebugCommand('backtrace'); + devtools.DebuggerAgent.sendCommand_(cmd); +}; + + +/** + * Sends command to v8 debugger. + * @param {devtools.DebugCommand} cmd Command to execute. + */ +devtools.DebuggerAgent.sendCommand_ = function(cmd) { + RemoteDebuggerCommandExecutor.DebuggerCommand(cmd.toJSONProtocol()); +}; + + +/** + * Tells the v8 debugger to make the next execution step. + * @param {string} action 'in', 'out' or 'next' action. + */ +devtools.DebuggerAgent.prototype.stepCommand_ = function(action) { + var cmd = new devtools.DebugCommand('continue', { + 'stepaction': action, + 'stepcount': 1 + }); + devtools.DebuggerAgent.sendCommand_(cmd); +}; + + +/** * Handles output sent by v8 debugger. The output is either asynchronous event * or response to a previously sent request. See protocol definitioun for more * details on the output format. @@ -47,9 +174,17 @@ devtools.DebuggerAgent.prototype.handleDebuggerOutput_ = function(output) { throw e; } - if (msg.getType() == 'response') { + if (msg.getType() == 'event') { + if (msg.getEvent() == 'break') { + this.handleBreakEvent_(msg); + } + } else if (msg.getType() == 'response') { if (msg.getCommand() == 'scripts') { this.handleScriptsResponse_(msg); + } else if (msg.getCommand() == 'setbreakpoint') { + this.handleSetBreakpointResponse_(msg); + } else if (msg.getCommand() == 'backtrace') { + this.handleBacktraceResponse_(msg); } } }; @@ -58,10 +193,30 @@ devtools.DebuggerAgent.prototype.handleDebuggerOutput_ = function(output) { /** * @param {devtools.DebuggerMessage} msg */ +devtools.DebuggerAgent.prototype.handleBreakEvent_ = function(msg) { + var body = msg.getBody(); + + this.currentCallFrame_ = { + 'sourceID': body.script.id, + 'line': body.sourceLine - body.script.lineOffset +1, + 'script': body.script + }; + + this.requestBacktrace_(); +}; + + +/** + * @param {devtools.DebuggerMessage} msg + */ devtools.DebuggerAgent.prototype.handleScriptsResponse_ = function(msg) { var scripts = msg.getBody(); for (var i = 0; i < scripts.length; i++) { var script = scripts[i]; + + this.parsedScripts_[script.id] = new devtools.ScriptInfo( + script.id, script.lineOffset); + WebInspector.parsedScriptSource( script.id, script.name, script.source, script.lineOffset); } @@ -69,6 +224,152 @@ devtools.DebuggerAgent.prototype.handleScriptsResponse_ = function(msg) { /** + * @param {devtools.DebuggerMessage} msg + */ +devtools.DebuggerAgent.prototype.handleSetBreakpointResponse_ = function(msg) { + var requestSeq = msg.getRequestSeq(); + var breakpointInfo = this.requestNumberToBreakpointInfo_[requestSeq]; + if (!breakpointInfo) { + // TODO(yurys): handle this case + return; + } + delete this.requestNumberToBreakpointInfo_[requestSeq]; + if (!msg.isSuccess()) { + // TODO(yurys): handle this case + return; + } + var idInV8 = msg.getBody().breakpoint; + breakpointInfo.setV8Id(idInV8); +}; + + +/** + * Handles response to 'backtrace' command. + * @param {devtools.DebuggerMessage} msg + */ +devtools.DebuggerAgent.prototype.handleBacktraceResponse_ = function(msg) { + if (!this.currentCallFrame_) { + return; + } + + var script = this.currentCallFrame_.script; + + var caller = null; + var f = null; + var frames = msg.getBody().frames; + for (var i = frames.length - 1; i>=0; i--) { + var nextFrame = frames[i]; + var func = msg.lookup(nextFrame.func.ref); + + // Format arguments. + var argv = []; + for (var j = 0; j < nextFrame.arguments.length; j++) { + var arg = nextFrame.arguments[j]; + var val = msg.lookup(arg.value.ref); + if (val) { + if (val.value) { + argv.push(arg.name + " = " + val.value); + } else { + argv.push(arg.name + " = [" + val.type + "]"); + } + + } else { + argv.push(arg.name + " = {ref:" + arg.value.ref + "} "); + } + } + + var funcName = func.name + "(" + argv.join(", ") + ")"; + + var f = { + 'sourceID': script.id, + 'line': nextFrame.line - script.lineOffset +1, + 'type': 'function', + 'functionName': funcName, //nextFrame.text, + 'caller': caller + }; + caller = f; + } + + this.currentCallFrame_ = f; + + WebInspector.pausedScript(); +}; + + +/** + * @param {number} scriptId Id of the script. + * @param {number} lineOffset First line 0-based offset in the containing + * document. + * @constructor + */ +devtools.ScriptInfo = function(scriptId, lineOffset) { + this.scriptId_ = scriptId; + this.lineOffset_ = lineOffset; + + this.lineToBreakpointInfo_ = {}; +}; + + +/** + * @return {number} + */ +devtools.ScriptInfo.prototype.getLineOffset = function() { + return this.lineOffset_; +}; + + +/** + * @param {number} line 0-based line number in the script. + * @return {?devtools.BreakpointInfo} Information on a breakpoint at the + * specified line in the script or undefined if there is no breakpoint at + * that line. + */ +devtools.ScriptInfo.prototype.getBreakpointInfo = function(line) { + return this.lineToBreakpointInfo_[line]; +}; + + +/** + * Adds breakpoint info to the script. + * @param {devtools.BreakpointInfo} breakpoint + */ +devtools.ScriptInfo.prototype.addBreakpointInfo = function(breakpoint) { + this.lineToBreakpointInfo_[breakpoint.getLine()] = breakpoint; +}; + + + +/** + * @param {number} scriptId Id of the owning script. + * @param {number} line Breakpoint 0-based line number in the containing script. + * @constructor + */ +devtools.BreakpointInfo = function(sourceId, line) { + this.sourceId_ = sourceId; + this.line_ = line; + this.v8id_ = -1; +}; + + +/** + * @return {number} + */ +devtools.BreakpointInfo.prototype.getLine = function(n) { + return this.line_; +}; + + +/** + * Sets id of this breakpoint in the v8 debugger. + * @param {number} id + */ +devtools.BreakpointInfo.prototype.setV8Id = function(id) { + this.v8id_ = id; +}; + + + +/** * JSON based commands sent to v8 debugger. * @param {string} command Name of the command to execute. * @param {Object} opt_arguments Command-specific arguments map. diff --git a/webkit/glue/devtools/js/inspector_controller_impl.js b/webkit/glue/devtools/js/inspector_controller_impl.js index d1af612..955b8ff 100644 --- a/webkit/glue/devtools/js/inspector_controller_impl.js +++ b/webkit/glue/devtools/js/inspector_controller_impl.js @@ -91,39 +91,47 @@ devtools.InspectorControllerImpl.prototype.inspectedWindow = function() { /** * @override */ -devtools.InspectorControllerImpl.prototype.debuggerEnabled = function() { - return true; -}; - - -devtools.InspectorControllerImpl.prototype.currentCallFrame = function() { - // TODO(yurys); - return null; -}; - - -devtools.InspectorControllerImpl.prototype.removeBreakpoint = function( - sourceID, line) { -}; - - -devtools.InspectorControllerImpl.prototype.resumeDebugger = function() { -}; - - -devtools.InspectorControllerImpl.prototype.stepIntoStatementInDebugger = - function() { -}; - - -devtools.InspectorControllerImpl.prototype.stepOutOfFunctionInDebugger = - function() { -}; - - -devtools.InspectorControllerImpl.prototype.stepOverStatementInDebugger = - function() { -}; +devtools.InspectorControllerImpl.prototype.debuggerEnabled = function() { + return true; +}; + + +devtools.InspectorControllerImpl.prototype.currentCallFrame = function() { + return devtools.tools.getDebuggerAgent().getCurrentCallFrame(); +}; + + +devtools.InspectorControllerImpl.prototype.addBreakpoint = function( + sourceID, line) { + devtools.tools.getDebuggerAgent().addBreakpoint(sourceID, line); +}; + + +devtools.InspectorControllerImpl.prototype.removeBreakpoint = function( + sourceID, line) { +}; + + +devtools.InspectorControllerImpl.prototype.resumeDebugger = function() { +}; + + +devtools.InspectorControllerImpl.prototype.stepIntoStatementInDebugger = + function() { + devtools.tools.getDebuggerAgent().stepIntoStatement(); +}; + + +devtools.InspectorControllerImpl.prototype.stepOutOfFunctionInDebugger = + function() { + devtools.tools.getDebuggerAgent().stepOutOfFunction(); +}; + + +devtools.InspectorControllerImpl.prototype.stepOverStatementInDebugger = + function() { + devtools.tools.getDebuggerAgent().stepOverStatement(); +}; var InspectorController = new devtools.InspectorControllerImpl(); diff --git a/webkit/glue/webdevtoolsclient_impl.cc b/webkit/glue/webdevtoolsclient_impl.cc index bcee3d2..231b4cf 100644 --- a/webkit/glue/webdevtoolsclient_impl.cc +++ b/webkit/glue/webdevtoolsclient_impl.cc @@ -7,10 +7,12 @@ #include #include "Document.h" +#include "DOMWindow.h" #include "InspectorController.h" #include "Node.h" #include "Page.h" #include "PlatformString.h" +#include "SecurityOrigin.h" #include #include #undef LOG @@ -36,6 +38,7 @@ using WebCore::Document; using WebCore::InspectorController; using WebCore::Node; using WebCore::Page; +using WebCore::SecurityOrigin; using WebCore::String; DEFINE_RPC_JS_BOUND_OBJ(DebuggerAgent, DEBUGGER_AGENT_STRUCT, @@ -158,6 +161,12 @@ void WebDevToolsClientImpl::JsLoaded( const CppArgumentList& args, CppVariant* result) { loaded_ = true; + + // Grant the devtools page the ability to have source view iframes. + Page* page = web_view_impl_->page(); + SecurityOrigin* origin = page->mainFrame()->domWindow()->securityOrigin(); + origin->grantUniversalAccess(); + for (Vector::iterator it = pending_incoming_messages_.begin(); it != pending_incoming_messages_.end(); ++it) { DispatchMessageFromAgent(*it); -- cgit v1.1