+2006-07-11 Timothy Hatcher <timothy@apple.com>
+
+ Reviewed by a tired Geoff.
+
+ Bug 9597: [Drosera] hook up the variables table to show stack variables
+ http://bugzilla.opendarwin.org/show_bug.cgi?id=9597
+
+ * Drosera/DebuggerDocument.m:
+ (-[WebScriptObject isSelectorExcludedFromWebScript:]):
+ (-[WebScriptObject webScriptAttributeKeysForScriptObject:]):
+ (-[WebScriptObject localScopeVariableNamesForCallFrame:]):
+ (-[WebScriptObject valueForScopeVariableNamed:inCallFrame:]):
+ (-[WebScriptObject webView:didReceiveTitle:forFrame:]):
+ (-[WebScriptObject webView:didLoadMainResourceForDataSource:]):
+ (-[WebScriptObject webView:didParseSource:baseLineNumber:fromURL:sourceId:forWebFrame:]):
+ (-[WebScriptObject webView:didEnterCallFrame:sourceId:line:forWebFrame:]):
+ (-[WebScriptObject webView:willExecuteStatement:sourceId:line:forWebFrame:]):
+ (-[WebScriptObject webView:willLeaveCallFrame:sourceId:line:forWebFrame:]):
+ * Drosera/debugger.css:
+ * Drosera/debugger.html:
+ * Drosera/debugger.js:
+
2006-07-10 Tim Omernick <timo@apple.com>
Reviewed by Beth Dakin.
static NSString *DebuggerStepOverToolbarItem = @"DebuggerStepOverToolbarItem";
static NSString *DebuggerStepOutToolbarItem = @"DebuggerStepOutToolbarItem";
+@interface WebScriptObject (WebScriptObjectPrivate)
+- (unsigned int)count;
+@end
+
@implementation DebuggerDocument
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
{
return [result autorelease];
}
+- (NSArray *)webScriptAttributeKeysForScriptObject:(WebScriptObject *)object
+{
+ WebScriptObject *func = [object evaluateWebScript:@"(function () { var result = new Array(); for (var x in this) { result.push(x); } return result; })"];
+ [object setValue:func forKey:@"__drosera_introspection"];
+
+ NSMutableArray *result = [[NSMutableArray alloc] init];
+ WebScriptObject *variables = [object callWebScriptMethod:@"__drosera_introspection" withArguments:nil];
+ unsigned length = [variables count];
+ for (unsigned i = 0; i < length; i++) {
+ NSString *key = [variables webScriptValueAtIndex:i];
+ if (![key isEqualToString:@"__drosera_introspection"])
+ [result addObject:key];
+ }
+
+ [object removeWebScriptKey:@"__drosera_introspection"];
+
+ [result sortUsingSelector:@selector(compare:)];
+ return [result autorelease];
+}
+
+- (NSArray *)localScopeVariableNamesForCallFrame:(int)frame
+{
+ WebScriptCallFrame *cframe = currentFrame;
+ for (unsigned count = 0; count < frame; count++)
+ cframe = [cframe caller];
+
+ if (![[cframe scopeChain] count])
+ return nil;
+
+ WebScriptObject *scope = [[cframe scopeChain] objectAtIndex:0]; // local is always first
+ return [self webScriptAttributeKeysForScriptObject:scope];
+}
+
+- (NSString *)valueForScopeVariableNamed:(NSString *)key inCallFrame:(int)frame
+{
+ WebScriptCallFrame *cframe = currentFrame;
+ for (unsigned count = 0; count < frame; count++)
+ cframe = [cframe caller];
+
+ if (![[cframe scopeChain] count])
+ return nil;
+
+ unsigned scopeCount = [[cframe scopeChain] count];
+ for (unsigned i = 0; i < scopeCount; i++) {
+ WebScriptObject *scope = [[cframe scopeChain] objectAtIndex:i];
+ id value = [scope valueForKey:key];
+ if ([value isKindOfClass:NSClassFromString(@"WebScriptObject")])
+ return [value callWebScriptMethod:@"toString" withArguments:nil];
+ if (value && ![value isKindOfClass:[NSString class]])
+ return [NSString stringWithFormat:@"%@", value];
+ if (value)
+ return value;
+ }
+
+ return nil;
+}
+
#pragma mark -
#pragma mark Pause & Step
return;
NSString *urlCopy = [[[[dataSource response] URL] absoluteString] copy];
- NSArray *args = [NSArray arrayWithObjects:(documentSourceCopy ? documentSourceCopy : @""), (urlCopy ? urlCopy : @""), [NSNumber numberWithBool:NO], nil];
+ NSArray *args = [[NSArray alloc] initWithObjects:(documentSourceCopy ? documentSourceCopy : @""), (urlCopy ? urlCopy : @""), [NSNumber numberWithBool:NO], nil];
[[webView windowScriptObject] callWebScriptMethod:@"updateFileSource" withArguments:args];
+ [args release];
[documentSourceCopy release];
[urlCopy release];
}
urlCopy = [[[[dataSource response] URL] absoluteString] copy];
}
- NSArray *args = [NSArray arrayWithObjects:sourceCopy, (documentSourceCopy ? documentSourceCopy : @""), (urlCopy ? urlCopy : @""), [NSNumber numberWithInt:sid], [NSNumber numberWithUnsignedInt:baseLine], nil];
+ NSArray *args = [[NSArray alloc] initWithObjects:sourceCopy, (documentSourceCopy ? documentSourceCopy : @""), (urlCopy ? urlCopy : @""), [NSNumber numberWithInt:sid], [NSNumber numberWithUnsignedInt:baseLine], nil];
[[webView windowScriptObject] callWebScriptMethod:@"didParseScript" withArguments:args];
+ [args release];
[sourceCopy release];
[documentSourceCopy release];
[urlCopy release];
currentFrame = [frame retain];
[old release];
- NSArray *args = [NSArray arrayWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
+ NSArray *args = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
[[webView windowScriptObject] callWebScriptMethod:@"didEnterCallFrame" withArguments:args];
+ [args release];
}
- (void)webView:(WebView *)view willExecuteStatement:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame
if (!webViewLoaded)
return;
- NSArray *args = [NSArray arrayWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
+ NSArray *args = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
[[webView windowScriptObject] callWebScriptMethod:@"willExecuteStatement" withArguments:args];
+ [args release];
}
- (void)webView:(WebView *)view willLeaveCallFrame:(WebScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno forWebFrame:(WebFrame *)webFrame
if (!webViewLoaded)
return;
- NSArray *args = [NSArray arrayWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
+ NSArray *args = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:sid], [NSNumber numberWithInt:lineno], nil];
[[webView windowScriptObject] callWebScriptMethod:@"willLeaveCallFrame" withArguments:args];
+ [args release];
id old = currentFrame;
currentFrame = [[frame caller] retain];
*/
img { padding: 0; margin: 0; }
-body { margin: 0; padding: 0; }
+body { margin: 0; padding: 0; overflow: hidden; }
#main { position: absolute; top: 0; bottom: 0; left: 0; right: 0; }
#info { position: absolute; top: 0; height: 175px; left: 0; right: 0; }
border: 0;
}
-td { padding: 3px 7px 3px 9px; height: 15px; box-sizing: border-box; }
+td {
+ padding: 3px 7px 3px 9px;
+ height: 15px;
+ box-sizing: border-box;
+ -webkit-user-select: none;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+tr.current {
+ background-color: rgb(56, 117, 215);
+ color: white;
+}
.stackNumber {
width: 2em;
<div id="infoDivider"></div>
<div id="variables">
<table id="variablesTableHeader">
-<tr class="column"><th class="variable" id ="variable" onmousedown="headerMouseDown(this);" onmouseup="headerMouseUp(this);" onmouseout="headerMouseOut(this);">Variable<div id="variableColumnResizer"></div>
+<tr class="column"><th class="variable" id="variable" onmousedown="headerMouseDown(this);" onmouseup="headerMouseUp(this);" onmouseout="headerMouseOut(this);">Variable<div id="variableColumnResizer"></div>
</th><th onmousedown="headerMouseDown(this);" onmouseup="headerMouseUp(this);" onmouseout="headerMouseOut(this);">Value</th><th class="scrollCorner"></th></tr>
</table>
<div id="variablesBody">
var currentFile = -1;
var currentRow = null;
var currentStack = null;
+var currentCallFrame = null;
var previousFiles = new Array();
var nextFiles = new Array();
var isResizingColumn = false;
var steppingStack = 0;
var pauseOnNextStatement = false;
+ScriptCallFrame = function (functionName, index, row)
+{
+ this.functionName = functionName;
+ this.index = index;
+ this.row = row;
+ this.localVariableNames = null;
+}
+
+ScriptCallFrame.prototype.valueForScopeVariable = function (name)
+{
+ return DebuggerDocument.valueForScopeVariableNamed_inCallFrame_(name, this.index);
+}
+
+ScriptCallFrame.prototype.loadVariables = function ()
+{
+ if (!this.localVariableNames)
+ this.localVariableNames = DebuggerDocument.localScopeVariableNamesForCallFrame_(this.index);
+
+ var variablesTable = document.getElementById("variablesTable");
+ variablesTable.innerHTML = "";
+
+ for(var i = 0; i < this.localVariableNames.length; i++) {
+ var tr = document.createElement("tr");
+ var td = document.createElement("td");
+ td.innerText = this.localVariableNames[i];
+ td.className = "variable";
+ tr.appendChild(td);
+
+ td = document.createElement("td");
+ td.innerText = this.valueForScopeVariable(this.localVariableNames[i]);
+ tr.appendChild(td);
+ tr.addEventListener("click", selectVariable, true);
+
+ variablesTable.appendChild(tr);
+ }
+}
+
function sleep(numberMillis) {
var now = new Date();
var exitTime = now.getTime() + numberMillis;
if (element.dragging == true) {
var main = document.getElementById("rightPane");
var variableColumn = document.getElementById("variable");
+ var rules = document.defaultView.getMatchedCSSRules(variableColumn, "");
+ for (var i = 0; i < rules.length; i++) {
+ if (rules[i].selectorText == ".variable") {
+ var columnRule = rules[i];
+ break;
+ }
+ }
+
var x = event.clientX + window.scrollX;
var delta = element.dragLastX - x;
var newWidth = constrainedWidthFromElement(variableColumn.clientWidth - delta, main);
if ((variableColumn.clientWidth - delta) == newWidth) // the width wasn't constrained
element.dragLastX = x;
- variableColumn.style.width = newWidth + "px";
+ columnRule.style.width = newWidth + "px";
element.style.left = newWidth + "px";
event.preventDefault();
}
currentRow = null;
}
- if (currentStack) {
- var stackframeTable = document.getElementById("stackframeTable");
- stackframeTable.innerHTML = ""; // clear the content
- currentStack = null;
- }
+ var stackframeTable = document.getElementById("stackframeTable");
+ stackframeTable.innerHTML = ""; // clear the content
+ var variablesTable = document.getElementById("variablesTable");
+ variablesTable.innerHTML = ""; // clear the content
+ currentStack = null;
+ currentCallFrame = null;
pauseOnNextStatement = false;
steppingOut = false;
var stackframeTable = document.getElementById("stackframeTable");
stackframeTable.innerHTML = ""; // clear the content
- currentStack = DebuggerDocument.currentFunctionStack();
- for(var i = 0; i < currentStack.length; i++) {
+ currentStack = new Array();
+ var stack = DebuggerDocument.currentFunctionStack();
+ for(var i = 0; i < stack.length; i++) {
var tr = document.createElement("tr");
var td = document.createElement("td");
td.className = "stackNumber";
tr.appendChild(td);
td = document.createElement("td");
- td.innerText = currentStack[i];
+ td.innerText = stack[i];
tr.appendChild(td);
+ tr.addEventListener("click", selectStackFrame, true);
stackframeTable.appendChild(tr);
- }
- var tr = document.createElement("tr");
- var td = document.createElement("td");
- td.className = "stackNumber";
- td.innerText = i;
- tr.appendChild(td);
+ var frame = new ScriptCallFrame(stack[i], i, tr);
+ tr.callFrame = frame;
+ currentStack.push(frame);
- td = document.createElement("td");
- td.innerText = "Global";
- tr.appendChild(td);
+ if (i == 0) {
+ addStyleClass(tr, "current");
+ frame.loadVariables();
+ currentCallFrame = frame;
+ }
+ }
+}
+
+function selectStackFrame(event)
+{
+ var stackframeTable = document.getElementById("stackframeTable");
+ var rows = stackframeTable.childNodes;
+ for (var i = 0; i < rows.length; i++)
+ removeStyleClass(rows[i], "current");
+ addStyleClass(this, "current");
+ this.callFrame.loadVariables();
+ currentCallFrame = this.callFrame;
+}
- stackframeTable.appendChild(tr);
+function selectVariable(event)
+{
+ var variablesTable = document.getElementById("variablesTable");
+ var rows = variablesTable.childNodes;
+ for (var i = 0; i < rows.length; i++)
+ removeStyleClass(rows[i], "current");
+ addStyleClass(this, "current");
}
function loadFile(fileIndex, manageNavLists)