summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/inspector/front-end/DebuggerPresentationModel.js
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/inspector/front-end/DebuggerPresentationModel.js')
-rw-r--r--Source/WebCore/inspector/front-end/DebuggerPresentationModel.js547
1 files changed, 497 insertions, 50 deletions
diff --git a/Source/WebCore/inspector/front-end/DebuggerPresentationModel.js b/Source/WebCore/inspector/front-end/DebuggerPresentationModel.js
index a97db34..d12affe 100644
--- a/Source/WebCore/inspector/front-end/DebuggerPresentationModel.js
+++ b/Source/WebCore/inspector/front-end/DebuggerPresentationModel.js
@@ -30,102 +30,549 @@
WebInspector.DebuggerPresentationModel = function()
{
- this._breakpoints = {};
- this._sourceLocationToBreakpointId = {};
+ this._sourceFiles = {};
+ this._messages = [];
+ this._presentationBreakpoints = {};
+ this._presentationCallFrames = [];
+ this._selectedCallFrameIndex = 0;
- WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointAdded, this._breakpointAdded, this);
- WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointRemoved, this._breakpointRemoved, this);
+ WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._debuggerWasEnabled, this);
+ WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
+ WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._failedToParseScriptSource, this);
WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointResolved, this._breakpointResolved, this);
+ WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
+ WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
+ WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.Reset, this._reset, this);
}
WebInspector.DebuggerPresentationModel.Events = {
+ SourceFileAdded: "source-file-added",
+ SourceFileChanged: "source-file-changed",
+ ConsoleMessageAdded: "console-message-added",
BreakpointAdded: "breakpoint-added",
- BreakpointRemoved: "breakpoint-removed"
+ BreakpointRemoved: "breakpoint-removed",
+ DebuggerPaused: "debugger-paused",
+ DebuggerResumed: "debugger-resumed",
+ CallFrameSelected: "call-frame-selected"
}
WebInspector.DebuggerPresentationModel.prototype = {
+ _debuggerWasEnabled: function()
+ {
+ this._restoreBreakpoints();
+ },
+
+ sourceFile: function(sourceFileId)
+ {
+ return this._sourceFiles[sourceFileId];
+ },
+
+ requestSourceFileContent: function(sourceFileId, callback)
+ {
+ this._sourceFiles[sourceFileId].requestContent(callback);
+ },
+
+ _parsedScriptSource: function(event)
+ {
+ this._addScript(event.data);
+ this._refreshBreakpoints();
+ },
+
+ _failedToParseScriptSource: function(event)
+ {
+ this._addScript(event.data);
+ this._refreshBreakpoints();
+ },
+
+ _addScript: function(script)
+ {
+ var sourceFileId = script.sourceURL || script.sourceID;
+ var sourceFile = this._sourceFiles[sourceFileId];
+ if (sourceFile) {
+ sourceFile.addScript(script);
+ return;
+ }
+
+ function contentChanged(sourceFile)
+ {
+ this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.SourceFileChanged, this._sourceFiles[sourceFileId]);
+ }
+ if (!this._formatSourceFiles)
+ sourceFile = new WebInspector.SourceFile(sourceFileId, script, contentChanged.bind(this));
+ else
+ sourceFile = new WebInspector.FormattedSourceFile(sourceFileId, script, contentChanged.bind(this), this._formatter);
+ this._sourceFiles[sourceFileId] = sourceFile;
+ this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.SourceFileAdded, sourceFile);
+ },
+
+ _refreshBreakpoints: function()
+ {
+ var breakpoints = WebInspector.debuggerModel.breakpoints;
+ for (var id in breakpoints) {
+ if (!(id in this._presentationBreakpoints))
+ this._breakpointAdded(breakpoints[id]);
+ }
+ },
+
+ canEditScriptSource: function(sourceFileId)
+ {
+ if (!Preferences.canEditScriptSource)
+ return false;
+ var script = this._scriptForSourceFileId(sourceFileId);
+ return !script.lineOffset && !script.columnOffset;
+ },
+
+ editScriptSource: function(sourceFileId, text, callback)
+ {
+ var script = this._scriptForSourceFileId(sourceFileId);
+ var sourceFile = this._sourceFiles[sourceFileId];
+ var oldSource = sourceFile.content;
+ function didEditScriptSource(success, newBodyOrErrorMessage)
+ {
+ if (!success) {
+ callback(false, newBodyOrErrorMessage);
+ return;
+ }
+
+ var newSource = newBodyOrErrorMessage;
+ this._updateBreakpointsAfterLiveEdit(sourceFileId, oldSource, newSource);
+
+ var resource = WebInspector.resourceForURL(script.sourceURL);
+ if (resource) {
+ var revertHandle = this.editScriptSource.bind(this, sourceFileId, oldSource, sourceFile.reload.bind(sourceFile));
+ resource.setContent(newSource, revertHandle);
+ }
+
+ callback(true, newSource);
+
+ if (WebInspector.debuggerModel.callFrames)
+ this._debuggerPaused();
+ }
+ WebInspector.debuggerModel.editScriptSource(script.sourceID, text, didEditScriptSource.bind(this));
+ },
+
+ _updateBreakpointsAfterLiveEdit: function(sourceFileId, oldSource, newSource)
+ {
+ // Clear and re-create breakpoints according to text diff.
+ var diff = Array.diff(oldSource.split("\n"), newSource.split("\n"));
+ for (var id in this._presentationBreakpoints) {
+ var breakpoint = this._presentationBreakpoints[id];
+ if (breakpoint.sourceFileId !== sourceFileId)
+ continue;
+ var lineNumber = breakpoint.lineNumber;
+ this.removeBreakpoint(sourceFileId, lineNumber);
+
+ var newLineNumber = diff.left[lineNumber].row;
+ if (newLineNumber === undefined) {
+ for (var i = lineNumber - 1; i >= 0; --i) {
+ if (diff.left[i].row === undefined)
+ continue;
+ var shiftedLineNumber = diff.left[i].row + lineNumber - i;
+ if (shiftedLineNumber < diff.right.length) {
+ var originalLineNumber = diff.right[shiftedLineNumber].row;
+ if (originalLineNumber === lineNumber || originalLineNumber === undefined)
+ newLineNumber = shiftedLineNumber;
+ }
+ break;
+ }
+ }
+ if (newLineNumber !== undefined)
+ this.setBreakpoint(sourceFileId, newLineNumber, breakpoint.condition, breakpoint.enabled);
+ }
+ },
+
+ toggleFormatSourceFiles: function()
+ {
+ this._formatSourceFiles = !this._formatSourceFiles;
+ if (this._formatSourceFiles && !this._formatter)
+ this._formatter = new WebInspector.ScriptFormatter();
+
+ var messages = this._messages;
+ this._sourceFiles = {};
+ this._messages = [];
+ this._presentationBreakpoints = {};
+
+ var scripts = WebInspector.debuggerModel.scripts;
+ for (var id in scripts)
+ this._addScript(scripts[id]);
+
+ for (var i = 0; i < messages.length; ++i)
+ this.addConsoleMessage(messages[i]);
+
+ this._refreshBreakpoints();
+
+ if (WebInspector.debuggerModel.callFrames)
+ this._debuggerPaused();
+ },
+
+ addConsoleMessage: function(message)
+ {
+ this._messages.push(message);
+
+ var sourceFile = this._sourceFileForScriptURL(message.url);
+ if (!sourceFile)
+ return;
+
+ function didRequestSourceMapping(mapping)
+ {
+ var presentationMessage = {};
+ presentationMessage.sourceFileId = sourceFile.id;
+ presentationMessage.lineNumber = mapping.scriptLocationToSourceLocation(message.line - 1, 0).lineNumber;
+ presentationMessage.originalMessage = message;
+ sourceFile.messages.push(presentationMessage);
+ this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.ConsoleMessageAdded, presentationMessage);
+ }
+ sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
+ },
+
+ clearConsoleMessages: function()
+ {
+ this._messages = [];
+ for (var id in this._sourceFiles)
+ this._sourceFiles[id].messages = [];
+ },
+
+ continueToLine: function(sourceFileId, lineNumber)
+ {
+ function didRequestSourceMapping(mapping)
+ {
+ var location = mapping.sourceLocationToScriptLocation(lineNumber, 0);
+ WebInspector.debuggerModel.continueToLocation(location.scriptId, location.lineNumber, location.columnNumber);
+ }
+ this._sourceFiles[sourceFileId].requestSourceMapping(didRequestSourceMapping.bind(this));
+ },
+
breakpointsForSourceFileId: function(sourceFileId)
{
+ var sourceFile = this.sourceFile(sourceFileId);
+ if (!sourceFile)
+ return [];
var breakpoints = [];
- for (var id in this._breakpoints) {
- var breakpoint = this._breakpoints[id];
- if (breakpoint.sourceFileId === sourceFileId)
- breakpoints.push(breakpoint);
- }
+ for (var lineNumber in sourceFile.breakpoints)
+ breakpoints.push(sourceFile.breakpoints[lineNumber]);
return breakpoints;
},
- _breakpointAdded: function(event)
+ setBreakpoint: function(sourceFileId, lineNumber, condition, enabled)
{
- var breakpoint = event.data;
- var location = breakpoint.locations.length ? breakpoint.locations[0] : breakpoint;
- var sourceLocation = this._actualLocationToSourceLocation(breakpoint.url || breakpoint.sourceID, location.lineNumber, location.columnNumber);
+ function didSetBreakpoint(breakpoint)
+ {
+ if (breakpoint) {
+ this._breakpointAdded(breakpoint);
+ this._saveBreakpoints();
+ }
+ }
- var encodedSourceLocation = this._encodeSourceLocation(sourceLocation.sourceFileId, sourceLocation.lineNumber);
- if (encodedSourceLocation in this._sourceLocationToBreakpointId) {
- // We can't show more than one breakpoint on a single source frame line. Remove newly added breakpoint.
- WebInspector.debuggerModel.removeBreakpoint(breakpoint.id);
- return;
+ function didRequestSourceMapping(mapping)
+ {
+ var location = mapping.sourceLocationToScriptLocation(lineNumber, 0);
+ var script = WebInspector.debuggerModel.scriptForSourceID(location.scriptId);
+ if (script.sourceURL)
+ WebInspector.debuggerModel.setBreakpoint(script.sourceURL, location.lineNumber, location.columnNumber, condition, enabled, didSetBreakpoint.bind(this));
+ else
+ WebInspector.debuggerModel.setBreakpointBySourceId(script.sourceID, location.lineNumber, location.columnNumber, condition, enabled, didSetBreakpoint.bind(this));
}
+ this._sourceFiles[sourceFileId].requestSourceMapping(didRequestSourceMapping.bind(this));
+ },
- var presentationBreakpoint = {
- sourceFileId: sourceLocation.sourceFileId,
- lineNumber: sourceLocation.lineNumber,
- url: breakpoint.url,
- resolved: !!breakpoint.locations.length,
- condition: breakpoint.condition,
- enabled: breakpoint.enabled
- };
+ setBreakpointEnabled: function(sourceFileId, lineNumber, enabled)
+ {
+ var breakpoint = this.removeBreakpoint(sourceFileId, lineNumber);
+ this.setBreakpoint(sourceFileId, lineNumber, breakpoint.condition, enabled);
+ },
- this._sourceLocationToBreakpointId[encodedSourceLocation] = breakpoint.id;
- this._breakpoints[breakpoint.id] = presentationBreakpoint;
+ updateBreakpoint: function(sourceFileId, lineNumber, condition, enabled)
+ {
+ this.removeBreakpoint(sourceFileId, lineNumber);
+ this.setBreakpoint(sourceFileId, lineNumber, condition, enabled);
+ },
- this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, presentationBreakpoint);
+ removeBreakpoint: function(sourceFileId, lineNumber)
+ {
+ var breakpoint = this.findBreakpoint(sourceFileId, lineNumber);
+ WebInspector.debuggerModel.removeBreakpoint(breakpoint._id);
+ this._breakpointRemoved(breakpoint._id);
+ this._saveBreakpoints();
+ return breakpoint;
+ },
+
+ findBreakpoint: function(sourceFileId, lineNumber)
+ {
+ var sourceFile = this.sourceFile(sourceFileId);
+ if (sourceFile)
+ return sourceFile.breakpoints[lineNumber];
},
- _breakpointRemoved: function(event)
+ _breakpointAdded: function(breakpoint)
{
- var breakpointId = event.data;
- var breakpoint = this._breakpoints[breakpointId];
- var encodedSourceLocation = this._encodeSourceLocation(breakpoint.sourceFileId, breakpoint.lineNumber);
- delete this._breakpoints[breakpointId];
- delete this._sourceLocationToBreakpointId[encodedSourceLocation];
+ var script;
+ if (breakpoint.url)
+ script = WebInspector.debuggerModel.scriptsForURL(breakpoint.url)[0];
+ else
+ script = WebInspector.debuggerModel.scriptForSourceID(breakpoint.sourceID);
+ if (!script)
+ return;
+
+ function didRequestSourceMapping(mapping)
+ {
+ var scriptLocation = breakpoint.locations.length ? breakpoint.locations[0] : breakpoint;
+ var sourceLocation = mapping.scriptLocationToSourceLocation(scriptLocation.lineNumber, scriptLocation.columnNumber);
+ var lineNumber = sourceLocation.lineNumber;
+
+ if (this.findBreakpoint(sourceFile.id, lineNumber)) {
+ // We can't show more than one breakpoint on a single source file line.
+ WebInspector.debuggerModel.removeBreakpoint(breakpoint.id);
+ return;
+ }
+
+ var presentationBreakpoint = new WebInspector.PresentationBreakpoint(breakpoint, sourceFile, lineNumber);
+ presentationBreakpoint._id = breakpoint.id;
+ this._presentationBreakpoints[breakpoint.id] = presentationBreakpoint;
+ sourceFile.breakpoints[lineNumber] = presentationBreakpoint;
+ this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, presentationBreakpoint);
+ }
+ var sourceFile = this._sourceFileForScript(script);
+ sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
+ },
+
+ _breakpointRemoved: function(breakpointId)
+ {
+ var breakpoint = this._presentationBreakpoints[breakpointId];
+ delete this._presentationBreakpoints[breakpointId];
+ var sourceFile = this.sourceFile(breakpoint.sourceFileId);
+ delete sourceFile.breakpoints[breakpoint.lineNumber];
this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, breakpoint);
},
_breakpointResolved: function(event)
{
var breakpoint = event.data;
- this._breakpointRemoved({ data: breakpoint.id });
- this._breakpointAdded({ data: breakpoint });
+ if (!(breakpoint.id in this._presentationBreakpoints))
+ return;
+ this._breakpointRemoved(breakpoint.id);
+ this._breakpointAdded(breakpoint);
+ },
+
+ _restoreBreakpoints: function()
+ {
+ function didSetBreakpoint(breakpoint)
+ {
+ if (breakpoint)
+ this._breakpointAdded(breakpoint);
+ }
+ var breakpoints = WebInspector.settings.breakpoints;
+ for (var i = 0; i < breakpoints.length; ++i) {
+ var breakpoint = breakpoints[i];
+ WebInspector.debuggerModel.setBreakpoint(breakpoint.url, breakpoint.lineNumber, breakpoint.columnNumber, breakpoint.condition, breakpoint.enabled, didSetBreakpoint.bind(this));
+ }
},
- _encodeSourceLocation: function(sourceFileId, lineNumber)
+ _saveBreakpoints: function()
{
- return sourceFileId + ":" + lineNumber;
+ var serializedBreakpoints = [];
+ var breakpoints = WebInspector.debuggerModel.breakpoints;
+ for (var id in breakpoints) {
+ var breakpoint = breakpoints[id];
+ if (!breakpoint.url)
+ continue;
+ var serializedBreakpoint = {};
+ serializedBreakpoint.url = breakpoint.url;
+ serializedBreakpoint.lineNumber = breakpoint.lineNumber;
+ serializedBreakpoint.columnNumber = breakpoint.columnNumber;
+ serializedBreakpoint.condition = breakpoint.condition;
+ serializedBreakpoint.enabled = breakpoint.enabled;
+ serializedBreakpoints.push(serializedBreakpoint);
+ }
+ WebInspector.settings.breakpoints = serializedBreakpoints;
},
- set selectedCallFrame(callFrame)
+ _debuggerPaused: function()
{
- this._selectedCallFrame = callFrame;
- if (!callFrame)
- return;
+ var callFrames = WebInspector.debuggerModel.callFrames;
+ this._presentationCallFrames = [];
+ for (var i = 0; i < callFrames.length; ++i) {
+ var callFrame = callFrames[i];
+ var sourceFile;
+ var script = WebInspector.debuggerModel.scriptForSourceID(callFrame.sourceID);
+ if (script)
+ sourceFile = this._sourceFileForScript(script);
+ this._presentationCallFrames.push(new WebInspector.PresenationCallFrame(callFrame, i, sourceFile));
+ }
+ var details = WebInspector.debuggerModel.debuggerPausedDetails;
+ this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerPaused, { callFrames: this._presentationCallFrames, details: details });
+
+ this.selectedCallFrame = this._presentationCallFrames[this._selectedCallFrameIndex];
+ },
+
+ _debuggerResumed: function()
+ {
+ this._presentationCallFrames = [];
+ this._selectedCallFrameIndex = 0;
+ this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerResumed);
+ },
- var script = WebInspector.debuggerModel.scriptForSourceID(callFrame.sourceID);
- callFrame.sourceLocation = this._actualLocationToSourceLocation(script.sourceURL || script.sourceID, callFrame.line, callFrame.column);
+ set selectedCallFrame(callFrame)
+ {
+ this._selectedCallFrameIndex = callFrame.index;
+ callFrame.select();
this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.CallFrameSelected, callFrame);
},
get selectedCallFrame()
{
- return this._selectedCallFrame;
+ return this._presentationCallFrames[this._selectedCallFrameIndex];
+ },
+
+ _sourceFileForScript: function(script)
+ {
+ return this._sourceFiles[script.sourceURL || script.sourceID];
+ },
+
+ _sourceFileForScriptURL: function(scriptURL)
+ {
+ return this._sourceFiles[scriptURL];
+ },
+
+ _scriptForSourceFileId: function(sourceFileId)
+ {
+ function filter(script)
+ {
+ return (script.sourceURL || script.sourceID) === sourceFileId;
+ }
+ return WebInspector.debuggerModel.queryScripts(filter)[0];
},
- _actualLocationToSourceLocation: function(sourceID, lineNumber, columnNumber)
+ _reset: function()
{
- // TODO: use source mapping to obtain source location.
- return { sourceFileId: sourceID, lineNumber: lineNumber, columnNumber: columnNumber };
+ this._sourceFiles = {};
+ this._messages = [];
+ this._presentationBreakpoints = {};
+ this._presentationCallFrames = [];
+ this._selectedCallFrameIndex = 0;
}
}
WebInspector.DebuggerPresentationModel.prototype.__proto__ = WebInspector.Object.prototype;
+
+WebInspector.PresentationBreakpoint = function(breakpoint, sourceFile, lineNumber)
+{
+ this._breakpoint = breakpoint;
+ this._sourceFile = sourceFile;
+ this._lineNumber = lineNumber;
+}
+
+WebInspector.PresentationBreakpoint.prototype = {
+ get sourceFileId()
+ {
+ return this._sourceFile.id;
+ },
+
+ get lineNumber()
+ {
+ return this._lineNumber;
+ },
+
+ get condition()
+ {
+ return this._breakpoint.condition;
+ },
+
+ get enabled()
+ {
+ return this._breakpoint.enabled;
+ },
+
+ get url()
+ {
+ return this._sourceFile.url;
+ },
+
+ get resolved()
+ {
+ return !!this._breakpoint.locations.length
+ },
+
+ loadSnippet: function(callback)
+ {
+ function didRequestContent(mimeType, content)
+ {
+ var lineEndings = content.lineEndings();
+ var snippet = "";
+ if (this.lineNumber < lineEndings.length)
+ snippet = content.substring(lineEndings[this.lineNumber - 1], lineEndings[this.lineNumber]);
+ callback(snippet);
+ }
+ this._sourceFile.requestContent(didRequestContent.bind(this));
+ }
+}
+
+WebInspector.PresenationCallFrame = function(callFrame, index, sourceFile)
+{
+ this._callFrame = callFrame;
+ this._index = index;
+ this._sourceFile = sourceFile;
+ this._script = WebInspector.debuggerModel.scriptForSourceID(callFrame.sourceID);
+}
+
+WebInspector.PresenationCallFrame.prototype = {
+ get functionName()
+ {
+ return this._callFrame.functionName;
+ },
+
+ get type()
+ {
+ return this._callFrame.type;
+ },
+
+ get isInternalScript()
+ {
+ return !this._script;
+ },
+
+ get url()
+ {
+ if (this._sourceFile)
+ return this._sourceFile.url;
+ },
+
+ get scopeChain()
+ {
+ return this._callFrame.scopeChain;
+ },
+
+ get index()
+ {
+ return this._index;
+ },
+
+ select: function()
+ {
+ if (this._sourceFile)
+ this._sourceFile.forceLoadContent(this._script);
+ },
+
+ evaluate: function(code, objectGroup, includeCommandLineAPI, callback)
+ {
+ function didEvaluateOnCallFrame(error, result)
+ {
+ callback(WebInspector.RemoteObject.fromPayload(result));
+ }
+ DebuggerAgent.evaluateOnCallFrame(this._callFrame.id, code, objectGroup, includeCommandLineAPI, didEvaluateOnCallFrame.bind(this));
+ },
+
+ sourceLocation: function(callback)
+ {
+ if (!this._sourceFile) {
+ callback(undefined, this._callFrame.line, this._callFrame.column);
+ return;
+ }
+
+ function didRequestSourceMapping(mapping)
+ {
+ var sourceLocation = mapping.scriptLocationToSourceLocation(this._callFrame.line, this._callFrame.column);
+ callback(this._sourceFile.id, sourceLocation.lineNumber, sourceLocation.columnNumber);
+ }
+ this._sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
+ }
+}