2008-05-22 Kevin McCullough <kmccullough@apple.com>
[WebKit-https.git] / WebCore / page / inspector / ScriptsPanel.js
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.ScriptsPanel = function()
27 {
28     WebInspector.Panel.call(this);
29
30     this.element.addStyleClass("scripts");
31
32     this.topStatusBar = document.createElement("div");
33     this.topStatusBar.className = "status-bar";
34     this.topStatusBar.id = "scripts-status-bar";
35     this.element.appendChild(this.topStatusBar);
36
37     this.backButton = document.createElement("button");
38     this.backButton.className = "status-bar-item";
39     this.backButton.id = "scripts-back";
40     this.backButton.title = WebInspector.UIString("Show the previous script resource.");
41     this.backButton.disabled = true;
42     this.backButton.appendChild(document.createElement("img"));
43     this.topStatusBar.appendChild(this.backButton);
44
45     this.forwardButton = document.createElement("button");
46     this.forwardButton.className = "status-bar-item";
47     this.forwardButton.id = "scripts-forward";
48     this.forwardButton.title = WebInspector.UIString("Show the next script resource.");
49     this.forwardButton.disabled = true;
50     this.forwardButton.appendChild(document.createElement("img"));
51     this.topStatusBar.appendChild(this.forwardButton);
52
53     this.filesSelectElement = document.createElement("select");
54     this.filesSelectElement.className = "status-bar-item";
55     this.filesSelectElement.id = "scripts-files";
56     this.filesSelectElement.addEventListener("change", this._changeVisibleFile.bind(this), false);
57     this.topStatusBar.appendChild(this.filesSelectElement);
58
59     this.functionsSelectElement = document.createElement("select");
60     this.functionsSelectElement.className = "status-bar-item";
61     this.functionsSelectElement.id = "scripts-functions";
62     this.topStatusBar.appendChild(this.functionsSelectElement);
63
64     this.sidebarButtonsElement = document.createElement("div");
65     this.sidebarButtonsElement.id = "scripts-sidebar-buttons";
66     this.topStatusBar.appendChild(this.sidebarButtonsElement);
67
68     this.pauseButton = document.createElement("button");
69     this.pauseButton.className = "status-bar-item";
70     this.pauseButton.id = "scripts-pause";
71     this.pauseButton.title = WebInspector.UIString("Pause script execution.");
72     this.pauseButton.disabled = true;
73     this.pauseButton.appendChild(document.createElement("img"));
74     this.pauseButton.addEventListener("click", this._togglePause.bind(this), false);
75     this.sidebarButtonsElement.appendChild(this.pauseButton);
76
77     this.stepOverButton = document.createElement("button");
78     this.stepOverButton.className = "status-bar-item";
79     this.stepOverButton.id = "scripts-step-over";
80     this.stepOverButton.title = WebInspector.UIString("Step over next function call.");
81     this.stepOverButton.disabled = true;
82     this.stepOverButton.addEventListener("click", this._stepOverClicked.bind(this), false);
83     this.stepOverButton.appendChild(document.createElement("img"));
84     this.sidebarButtonsElement.appendChild(this.stepOverButton);
85
86     this.stepIntoButton = document.createElement("button");
87     this.stepIntoButton.className = "status-bar-item";
88     this.stepIntoButton.id = "scripts-step-into";
89     this.stepIntoButton.title = WebInspector.UIString("Step into next function call.");
90     this.stepIntoButton.disabled = true;
91     this.stepIntoButton.addEventListener("click", this._stepIntoClicked.bind(this), false);
92     this.stepIntoButton.appendChild(document.createElement("img"));
93     this.sidebarButtonsElement.appendChild(this.stepIntoButton);
94
95     this.stepOutButton = document.createElement("button");
96     this.stepOutButton.className = "status-bar-item";
97     this.stepOutButton.id = "scripts-step-out";
98     this.stepOutButton.title = WebInspector.UIString("Step out of current function.");
99     this.stepOutButton.disabled = true;
100     this.stepOutButton.addEventListener("click", this._stepOutClicked.bind(this), false);
101     this.stepOutButton.appendChild(document.createElement("img"));
102     this.sidebarButtonsElement.appendChild(this.stepOutButton);
103
104     this.debuggerStatusElement = document.createElement("div");
105     this.debuggerStatusElement.id = "scripts-debugger-status";
106     this.sidebarButtonsElement.appendChild(this.debuggerStatusElement);
107
108     this.scriptResourceViews = document.createElement("div");
109     this.scriptResourceViews.id = "script-resource-views";
110
111     this.sidebarElement = document.createElement("div");
112     this.sidebarElement.id = "scripts-sidebar";
113
114     this.sidebarResizeElement = document.createElement("div");
115     this.sidebarResizeElement.className = "sidebar-resizer-vertical";
116     this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false);
117
118     this.sidebarResizeWidgetElement = document.createElement("div");
119     this.sidebarResizeWidgetElement.id = "scripts-sidebar-resizer-widget";
120     this.sidebarResizeWidgetElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false);
121     this.topStatusBar.appendChild(this.sidebarResizeWidgetElement);
122
123     this.sidebarPanes = {};
124     this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane();
125     this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane();
126     this.sidebarPanes.breakpoints = new WebInspector.BreakpointsSidebarPane();
127
128     for (var pane in this.sidebarPanes)
129         this.sidebarElement.appendChild(this.sidebarPanes[pane].element);
130
131     this.sidebarPanes.callstack.expanded = true;
132     this.sidebarPanes.callstack.addEventListener("call frame selected", this._callFrameSelected, this);
133
134     this.sidebarPanes.scopechain.expanded = true;
135
136     this.attachOverlayElement = document.createElement("div");
137     this.attachOverlayElement.id = "scripts-attach-overlay";
138
139     var headerElement = document.createElement("h1");
140     headerElement.textContent = WebInspector.UIString("Debugging scripts requires you to attach the debugger.");
141     this.attachOverlayElement.appendChild(headerElement);
142
143     var infoElement = document.createElement("span");
144     infoElement.textContent = WebInspector.UIString("Attaching will reload the inspected page.");
145     this.attachOverlayElement.appendChild(infoElement);
146
147     this.attachOverlayElement.appendChild(document.createElement("br"));
148     this.attachOverlayElement.appendChild(document.createElement("br"));
149
150     var attachButton = document.createElement("button");
151     attachButton.textContent = WebInspector.UIString("Attach Debugger");
152     attachButton.addEventListener("click", this._toggleDebugging.bind(this), false);
153     this.attachOverlayElement.appendChild(attachButton);
154
155     this.element.appendChild(this.attachOverlayElement);
156     this.element.appendChild(this.scriptResourceViews);
157     this.element.appendChild(this.sidebarElement);
158     this.element.appendChild(this.sidebarResizeElement);
159
160     this.debuggingButton = document.createElement("button");
161     this.debuggingButton.id = "scripts-debugging-status-bar-item";
162     this.debuggingButton.className = "status-bar-item";
163     this.debuggingButton.addEventListener("click", this._toggleDebugging.bind(this), false);
164
165     this.pauseOnExceptionButtons = document.createElement("button");
166     this.pauseOnExceptionButtons.id = "scripts-pause-on-exceptions-status-bar-item";
167     this.pauseOnExceptionButtons.className = "status-bar-item";
168     this.pauseOnExceptionButtons.addEventListener("click", this._togglePauseOnExceptions.bind(this), false);
169
170     this._breakpointsURLMap = {};
171
172     this.reset();
173 }
174
175 WebInspector.ScriptsPanel.prototype = {
176     toolbarItemClass: "scripts",
177
178     get toolbarItemLabel()
179     {
180         return WebInspector.UIString("Scripts");
181     },
182
183     get statusBarItems()
184     {
185         return [this.debuggingButton, this.pauseOnExceptionButtons];
186     },
187
188     get paused()
189     {
190         return this._paused;
191     },
192
193     show: function()
194     {
195         WebInspector.Panel.prototype.show.call(this);
196         this.sidebarResizeElement.style.right = (this.sidebarElement.offsetWidth - 3) + "px";
197
198         if (this.visibleView) {
199             if (this.visibleView instanceof WebInspector.ResourceView)
200                 this.visibleView.headersVisible = false;
201             this.visibleView.show(this.scriptResourceViews);
202         }
203     },
204
205     addScript: function(sourceID, sourceURL, source, startingLine, errorLine, errorMessage)
206     {
207         var script = new WebInspector.Script(sourceID, sourceURL, source, startingLine, errorLine, errorMessage);
208
209         if (sourceURL in WebInspector.resourceURLMap) {
210             var resource = WebInspector.resourceURLMap[sourceURL];
211             resource.addScript(script);
212         }
213
214         if (sourceURL in this._breakpointsURLMap && sourceID) {
215             var breakpoints = this._breakpointsURLMap[sourceURL];
216             var breakpointsLength = breakpoints.length;
217             for (var i = 0; i < breakpointsLength; ++i) {
218                 var breakpoint = breakpoints[i];
219                 if (startingLine <= breakpoint.line)
220                     breakpoint.sourceID = sourceID;
221             }
222
223             InspectorController.addBreakpoint(breakpoint.sourceID, breakpoint.line);
224         }
225
226         if (sourceID)
227             this._sourceIDMap[sourceID] = (resource || script);
228
229         this._addScriptToFilesMenu(script);
230     },
231
232     addBreakpoint: function(breakpoint)
233     {
234         this.sidebarPanes.breakpoints.addBreakpoint(breakpoint);
235
236         var sourceFrame;
237         if (breakpoint.url) {
238             if (!(breakpoint.url in this._breakpointsURLMap))
239                 this._breakpointsURLMap[breakpoint.url] = [];
240             this._breakpointsURLMap[breakpoint.url].unshift(breakpoint);
241
242             if (breakpoint.url in WebInspector.resourceURLMap) {
243                 var resource = WebInspector.resourceURLMap[breakpoint.url];
244                 sourceFrame = this._sourceFrameForScriptOrResource(resource);
245             }
246         }
247
248         if (breakpoint.sourceID && !sourceFrame) {
249             var object = this._sourceIDMap[breakpoint.sourceID]
250             sourceFrame = this._sourceFrameForScriptOrResource(object);
251         }
252
253         if (sourceFrame)
254             sourceFrame.addBreakpoint(breakpoint);
255     },
256
257     removeBreakpoint: function(breakpoint)
258     {
259         this.sidebarPanes.breakpoints.removeBreakpoint(breakpoint);
260
261         var sourceFrame;
262         if (breakpoint.url && breakpoint.url in this._breakpointsURLMap) {
263             var breakpoints = this._breakpointsURLMap[breakpoint.url];
264             breakpoints.remove(breakpoint);
265             if (!breakpoints.length)
266                 delete this._breakpointsURLMap[breakpoint.url];
267
268             if (breakpoint.url in WebInspector.resourceURLMap) {
269                 var resource = WebInspector.resourceURLMap[breakpoint.url];
270                 sourceFrame = this._sourceFrameForScriptOrResource(resource);
271             }
272         }
273
274         if (breakpoint.sourceID && !sourceFrame) {
275             var object = this._sourceIDMap[breakpoint.sourceID]
276             sourceFrame = this._sourceFrameForScriptOrResource(object);
277         }
278
279         if (sourceFrame)
280             sourceFrame.removeBreakpoint(breakpoint);
281     },
282
283     evaluateInSelectedCallFrame: function(code)
284     {
285         var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame;
286         if (!this._paused || !selectedCallFrame)
287             return;
288         var result = selectedCallFrame.evaluate(code);
289         this.sidebarPanes.scopechain.update(selectedCallFrame);
290         return result;
291     },
292
293     debuggerPaused: function()
294     {
295         this._paused = true;
296         this._waitingToPause = false;
297         this._stepping = false;
298
299         this._updateDebuggerButtons();
300
301         var callStackPane = this.sidebarPanes.callstack;
302         var currentFrame = InspectorController.currentCallFrame();
303         callStackPane.update(currentFrame);
304         callStackPane.selectedCallFrame = currentFrame;
305     },
306
307     debuggerAttached: function()
308     {
309         this.reset();
310     },
311
312     debuggerDetached: function()
313     {
314         this.reset();
315     },
316
317     reset: function()
318     {
319         this.visibleView = null;
320
321         if (!InspectorController.debuggerAttached()) {
322             this._paused = false;
323             this._waitingToPause = false;
324             this._stepping = false;
325         }
326
327         this._clearInterface();
328
329         this._scriptsForURLsInFilesSelect = {};
330         this.filesSelectElement.removeChildren();
331         this.functionsSelectElement.removeChildren();
332         this.scriptResourceViews.removeChildren();
333
334         if (this._sourceIDMap) {
335             for (var sourceID in this._sourceIDMap) {
336                 var object = this._sourceIDMap[sourceID];
337                 if (object instanceof WebInspector.Resource)
338                     object.removeAllScripts();
339             }
340         }
341
342         this._sourceIDMap = {};
343     },
344
345     get visibleView()
346     {
347         return this._visibleView;
348     },
349
350     set visibleView(x)
351     {
352         if (this._visibleView === x)
353             return;
354
355         if (this._visibleView)
356             this._visibleView.hide();
357
358         this._visibleView = x;
359
360         if (x)
361             x.show(this.scriptResourceViews);
362     },
363
364     showScript: function(script, line)
365     {
366         this._showScriptOrResource(script, line);
367     },
368
369     showResource: function(resource, line)
370     {
371         this._showScriptOrResource(resource, line);
372     },
373
374     scriptViewForScript: function(script)
375     {
376         if (!script)
377             return null;
378         if (!script._scriptView)
379             script._scriptView = new WebInspector.ScriptView(script);
380         return script._scriptView;
381     },
382
383     sourceFrameForScript: function(script)
384     {
385         var view = this.scriptViewForScript(script);
386         if (!view)
387             return null;
388
389         // Setting up the source frame requires that we be attached.
390         if (!this.element.parentNode)
391             this.attach();
392
393         view.setupSourceFrameIfNeeded();
394         return view.sourceFrame;
395     },
396
397     _sourceFrameForScriptOrResource: function(scriptOrResource)
398     {
399         if (scriptOrResource instanceof WebInspector.Resource)
400             return WebInspector.panels.resources.sourceFrameForResource(scriptOrResource);
401         if (scriptOrResource instanceof WebInspector.Script)
402             return this.sourceFrameForScript(scriptOrResource);
403     },
404
405     _showScriptOrResource: function(scriptOrResource, line)
406     {
407         if (!scriptOrResource)
408             return;
409
410         var view;
411         if (scriptOrResource instanceof WebInspector.Resource) {
412             view = WebInspector.panels.resources.resourceViewForResource(scriptOrResource);
413             view.headersVisible = false;
414
415             if (scriptOrResource.url in this._breakpointsURLMap) {
416                 var sourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource);
417                 if (sourceFrame && !sourceFrame.breakpoints.length) {
418                     var breakpoints = this._breakpointsURLMap[scriptOrResource.url];
419                     var breakpointsLength = breakpoints.length;
420                     for (var i = 0; i < breakpointsLength; ++i)
421                         sourceFrame.addBreakpoint(breakpoints[i]);
422                 }
423             }
424         } else if (scriptOrResource instanceof WebInspector.Script)
425             view = this.scriptViewForScript(scriptOrResource);
426
427         if (!view)
428             return;
429
430         this.visibleView = view;
431
432         if (line && view.revealLine)
433             view.revealLine(line);
434
435         var option;
436         if (scriptOrResource instanceof WebInspector.Script) {
437             option = script.filesSelectOption;
438             console.assert(option);
439         } else {
440             var url = scriptOrResource.url;
441             var script = this._scriptsForURLsInFilesSelect[url];
442             if (script)
443                option = script.filesSelectOption;
444         }
445
446         if (option)
447             this.filesSelectElement.selectedIndex = option.index;
448     },
449
450     _addScriptToFilesMenu: function(script)
451     {
452         if (script.resource && this._scriptsForURLsInFilesSelect[script.sourceURL])
453             return;
454
455         this._scriptsForURLsInFilesSelect[script.sourceURL] = script;
456
457         var select = this.filesSelectElement;
458
459         // FIXME: Append in some meaningful order.
460         var option = document.createElement("option");
461         option.representedObject = (script.resource || script);
462         option.text = (script.sourceURL ? script.sourceURL.trimURL(WebInspector.mainResource ? WebInspector.mainResource.domain : "") : "(eval script)");
463         select.appendChild(option);
464
465         script.filesSelectOption = option;
466
467         // Call _showScriptOrResource if the option we just appended ended up being selected.
468         // This will happen for the first item added to the menu.
469         if (select.options[select.selectedIndex] === option)
470             this._showScriptOrResource(option.representedObject);
471     },
472
473     _clearCurrentExecutionLine: function()
474     {
475         if (this._executionSourceFrame)
476             this._executionSourceFrame.executionLine = 0;
477         delete this._executionSourceFrame;
478     },
479
480     _callFrameSelected: function()
481     {
482         this._clearCurrentExecutionLine();
483
484         var callStackPane = this.sidebarPanes.callstack;
485         var currentFrame = callStackPane.selectedCallFrame;
486         if (!currentFrame)
487             return;
488
489         this.sidebarPanes.scopechain.update(currentFrame);
490
491         var scriptOrResource = this._sourceIDMap[currentFrame.sourceIdentifier];
492         this._showScriptOrResource(scriptOrResource, currentFrame.line);
493
494         this._executionSourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource);
495         if (this._executionSourceFrame)
496             this._executionSourceFrame.executionLine = currentFrame.line;
497     },
498
499     _changeVisibleFile: function(event)
500     {
501         var select = this.filesSelectElement;
502         this._showScriptOrResource(select.options[select.selectedIndex].representedObject);
503     },
504
505     _startSidebarResizeDrag: function(event)
506     {
507         WebInspector.elementDragStart(this.sidebarElement, this._sidebarResizeDrag.bind(this), this._endSidebarResizeDrag.bind(this), event, "col-resize");
508
509         if (event.target === this.sidebarResizeWidgetElement)
510             this._dragOffset = (event.target.offsetWidth - (event.pageX - event.target.totalOffsetLeft));
511         else
512             this._dragOffset = 0;
513     },
514
515     _endSidebarResizeDrag: function(event)
516     {
517         WebInspector.elementDragEnd(event);
518
519         delete this._dragOffset;
520     },
521
522     _sidebarResizeDrag: function(event)
523     {
524         var x = event.pageX + this._dragOffset;
525         var newWidth = Number.constrain(window.innerWidth - x, Preferences.minScriptsSidebarWidth, window.innerWidth * 0.66);
526
527         this.sidebarElement.style.width = newWidth + "px";
528         this.sidebarButtonsElement.style.width = newWidth + "px";
529         this.scriptResourceViews.style.right = newWidth + "px";
530         this.sidebarResizeWidgetElement.style.right = newWidth + "px";
531         this.sidebarResizeElement.style.right = (newWidth - 3) + "px";
532
533         event.preventDefault();
534     },
535
536     _updatePauseOnExceptionsButton: function()
537     {
538         if (InspectorController.pauseOnExceptions()) {
539             this.pauseOnExceptionButtons.title = WebInspector.UIString("Pause on exceptions.");
540             this.pauseOnExceptionButtons.addStyleClass("toggled-on");
541         } else {
542             this.pauseOnExceptionButtons.title = WebInspector.UIString("Don't pause on exceptions.");
543             this.pauseOnExceptionButtons.removeStyleClass("toggled-on");
544         }
545     },
546
547     _updateDebuggerButtons: function()
548     {
549         if (InspectorController.debuggerAttached()) {
550             this.debuggingButton.title = WebInspector.UIString("Stop debugging.");
551             this.debuggingButton.addStyleClass("toggled-on");
552             this.pauseButton.disabled = false;
553
554             if (this.attachOverlayElement.parentNode)
555                 this.attachOverlayElement.parentNode.removeChild(this.attachOverlayElement);
556         } else {
557             this.debuggingButton.title = WebInspector.UIString("Start debugging and reload inspected page.");
558             this.debuggingButton.removeStyleClass("toggled-on");
559             this.pauseButton.disabled = true;
560
561             this.element.appendChild(this.attachOverlayElement);
562         }
563
564         this._updatePauseOnExceptionsButton();
565
566         if (this._paused) {
567             this.pauseButton.addStyleClass("paused");
568
569             this.pauseButton.disabled = false;
570             this.stepOverButton.disabled = false;
571             this.stepIntoButton.disabled = false;
572             this.stepOutButton.disabled = false;
573
574             this.debuggerStatusElement.textContent = WebInspector.UIString("Paused");
575         } else {
576             this.pauseButton.removeStyleClass("paused");
577
578             this.pauseButton.disabled = this._waitingToPause;
579             this.stepOverButton.disabled = true;
580             this.stepIntoButton.disabled = true;
581             this.stepOutButton.disabled = true;
582
583             if (this._waitingToPause)
584                 this.debuggerStatusElement.textContent = WebInspector.UIString("Pausing");
585             else if (this._stepping)
586                 this.debuggerStatusElement.textContent = WebInspector.UIString("Stepping");
587             else
588                 this.debuggerStatusElement.textContent = "";
589         }
590     },
591
592     _clearInterface: function()
593     {
594         this.sidebarPanes.callstack.update(null);
595         this.sidebarPanes.scopechain.update(null);
596
597         this._clearCurrentExecutionLine();
598         this._updateDebuggerButtons();
599     },
600
601     _toggleDebugging: function()
602     {
603         this._paused = false;
604         this._waitingToPause = false;
605         this._stepping = false;
606
607         if (InspectorController.debuggerAttached())
608             InspectorController.stopDebugging();
609         else
610             InspectorController.startDebuggingAndReloadInspectedPage();
611
612         this._clearInterface();
613     },
614
615     _togglePauseOnExceptions: function()
616     {
617         InspectorController.setPauseOnExceptions(!InspectorController.pauseOnExceptions());
618         this._updatePauseOnExceptionsButton();
619     },
620
621     _togglePause: function()
622     {
623         if (this._paused) {
624             this._paused = false;
625             this._waitingToPause = false;
626             InspectorController.resumeDebugger();
627         } else {
628             this._stepping = false;
629             this._waitingToPause = true;
630             InspectorController.pauseInDebugger();
631         }
632
633         this._clearInterface();
634     },
635
636     _stepOverClicked: function()
637     {
638         this._paused = false;
639         this._stepping = true;
640
641         this._clearInterface();
642
643         InspectorController.stepOverStatementInDebugger();
644     },
645
646     _stepIntoClicked: function()
647     {
648         this._paused = false;
649         this._stepping = true;
650
651         this._clearInterface();
652
653         InspectorController.stepIntoStatementInDebugger();
654     },
655
656     _stepOutClicked: function()
657     {
658         this._paused = false;
659         this._stepping = true;
660
661         this._clearInterface();
662
663         InspectorController.stepOutOfFunctionInDebugger();
664     }
665 }
666
667 WebInspector.ScriptsPanel.prototype.__proto__ = WebInspector.Panel.prototype;