2010-12-22 Sheriff Bot <webkit.review.bot@gmail.com>
[WebKit-https.git] / WebCore / inspector / front-end / 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, "scripts");
29
30     this.topStatusBar = document.createElement("div");
31     this.topStatusBar.className = "status-bar";
32     this.topStatusBar.id = "scripts-status-bar";
33     this.element.appendChild(this.topStatusBar);
34
35     this.backButton = document.createElement("button");
36     this.backButton.className = "status-bar-item";
37     this.backButton.id = "scripts-back";
38     this.backButton.title = WebInspector.UIString("Show the previous script resource.");
39     this.backButton.disabled = true;
40     this.backButton.appendChild(document.createElement("img"));
41     this.backButton.addEventListener("click", this._goBack.bind(this), false);
42     this.topStatusBar.appendChild(this.backButton);
43
44     this.forwardButton = document.createElement("button");
45     this.forwardButton.className = "status-bar-item";
46     this.forwardButton.id = "scripts-forward";
47     this.forwardButton.title = WebInspector.UIString("Show the next script resource.");
48     this.forwardButton.disabled = true;
49     this.forwardButton.appendChild(document.createElement("img"));
50     this.forwardButton.addEventListener("click", this._goForward.bind(this), false);
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
63     // FIXME: append the functions select element to the top status bar when it is implemented.
64     // this.topStatusBar.appendChild(this.functionsSelectElement);
65
66     this.sidebarButtonsElement = document.createElement("div");
67     this.sidebarButtonsElement.id = "scripts-sidebar-buttons";
68     this.topStatusBar.appendChild(this.sidebarButtonsElement);
69
70     this.pauseButton = document.createElement("button");
71     this.pauseButton.className = "status-bar-item";
72     this.pauseButton.id = "scripts-pause";
73     this.pauseButton.title = WebInspector.UIString("Pause script execution.");
74     this.pauseButton.disabled = true;
75     this.pauseButton.appendChild(document.createElement("img"));
76     this.pauseButton.addEventListener("click", this._togglePause.bind(this), false);
77     this.sidebarButtonsElement.appendChild(this.pauseButton);
78
79     this.stepOverButton = document.createElement("button");
80     this.stepOverButton.className = "status-bar-item";
81     this.stepOverButton.id = "scripts-step-over";
82     this.stepOverButton.title = WebInspector.UIString("Step over next function call.");
83     this.stepOverButton.disabled = true;
84     this.stepOverButton.addEventListener("click", this._stepOverClicked.bind(this), false);
85     this.stepOverButton.appendChild(document.createElement("img"));
86     this.sidebarButtonsElement.appendChild(this.stepOverButton);
87
88     this.stepIntoButton = document.createElement("button");
89     this.stepIntoButton.className = "status-bar-item";
90     this.stepIntoButton.id = "scripts-step-into";
91     this.stepIntoButton.title = WebInspector.UIString("Step into next function call.");
92     this.stepIntoButton.disabled = true;
93     this.stepIntoButton.addEventListener("click", this._stepIntoClicked.bind(this), false);
94     this.stepIntoButton.appendChild(document.createElement("img"));
95     this.sidebarButtonsElement.appendChild(this.stepIntoButton);
96
97     this.stepOutButton = document.createElement("button");
98     this.stepOutButton.className = "status-bar-item";
99     this.stepOutButton.id = "scripts-step-out";
100     this.stepOutButton.title = WebInspector.UIString("Step out of current function.");
101     this.stepOutButton.disabled = true;
102     this.stepOutButton.addEventListener("click", this._stepOutClicked.bind(this), false);
103     this.stepOutButton.appendChild(document.createElement("img"));
104     this.sidebarButtonsElement.appendChild(this.stepOutButton);
105
106     this.toggleBreakpointsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Deactivate all breakpoints."), "toggle-breakpoints");
107     this.toggleBreakpointsButton.toggled = true;
108     this.toggleBreakpointsButton.addEventListener("click", this.toggleBreakpointsClicked.bind(this), false);
109     this.sidebarButtonsElement.appendChild(this.toggleBreakpointsButton.element);
110
111     this.debuggerStatusElement = document.createElement("div");
112     this.debuggerStatusElement.id = "scripts-debugger-status";
113     this.sidebarButtonsElement.appendChild(this.debuggerStatusElement);
114
115     this.viewsContainerElement = document.createElement("div");
116     this.viewsContainerElement.id = "script-resource-views";
117
118     this.sidebarElement = document.createElement("div");
119     this.sidebarElement.id = "scripts-sidebar";
120
121     this.sidebarResizeElement = document.createElement("div");
122     this.sidebarResizeElement.className = "sidebar-resizer-vertical";
123     this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false);
124
125     this.sidebarResizeWidgetElement = document.createElement("div");
126     this.sidebarResizeWidgetElement.id = "scripts-sidebar-resizer-widget";
127     this.sidebarResizeWidgetElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false);
128     this.topStatusBar.appendChild(this.sidebarResizeWidgetElement);
129
130     this.sidebarPanes = {};
131     this.sidebarPanes.watchExpressions = new WebInspector.WatchExpressionsSidebarPane();
132     this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane();
133     this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane();
134     this.sidebarPanes.jsBreakpoints = WebInspector.createJSBreakpointsSidebarPane();
135     if (Preferences.nativeInstrumentationEnabled) {
136         this.sidebarPanes.domBreakpoints = WebInspector.createDOMBreakpointsSidebarPane();
137         this.sidebarPanes.xhrBreakpoints = WebInspector.createXHRBreakpointsSidebarPane();
138         this.sidebarPanes.eventListenerBreakpoints = new WebInspector.EventListenerBreakpointsSidebarPane();
139     }
140
141     this.sidebarPanes.workers = new WebInspector.WorkersSidebarPane();
142
143     for (var pane in this.sidebarPanes)
144         this.sidebarElement.appendChild(this.sidebarPanes[pane].element);
145
146     this.sidebarPanes.callstack.expanded = true;
147     this.sidebarPanes.callstack.addEventListener("call frame selected", this._callFrameSelected, this);
148
149     this.sidebarPanes.scopechain.expanded = true;
150     this.sidebarPanes.jsBreakpoints.expanded = true;
151
152     var panelEnablerHeading = WebInspector.UIString("You need to enable debugging before you can use the Scripts panel.");
153     var panelEnablerDisclaimer = WebInspector.UIString("Enabling debugging will make scripts run slower.");
154     var panelEnablerButton = WebInspector.UIString("Enable Debugging");
155
156     this.panelEnablerView = new WebInspector.PanelEnablerView("scripts", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton);
157     this.panelEnablerView.addEventListener("enable clicked", this._enableDebugging, this);
158
159     this.element.appendChild(this.panelEnablerView.element);
160     this.element.appendChild(this.viewsContainerElement);
161     this.element.appendChild(this.sidebarElement);
162     this.element.appendChild(this.sidebarResizeElement);
163
164     this.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item");
165     this.enableToggleButton.addEventListener("click", this._toggleDebugging.bind(this), false);
166     if (Preferences.debuggerAlwaysEnabled)
167         this.enableToggleButton.element.addStyleClass("hidden");
168
169     this._pauseOnExceptionButton = new WebInspector.StatusBarButton("", "scripts-pause-on-exceptions-status-bar-item", 3);
170     this._pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions.bind(this), false);
171     this._pauseOnExceptionButton.state = WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions;
172     this._pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions.");
173
174     this._registerShortcuts();
175
176     this._debuggerEnabled = Preferences.debuggerAlwaysEnabled;
177
178     this.reset();
179
180     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
181     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._failedToParseScriptSource, this);
182     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
183     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
184 }
185
186 // Keep these in sync with WebCore::ScriptDebugServer
187 WebInspector.ScriptsPanel.PauseOnExceptionsState = {
188     DontPauseOnExceptions : 0,
189     PauseOnAllExceptions : 1,
190     PauseOnUncaughtExceptions: 2
191 };
192
193 WebInspector.ScriptsPanel.prototype = {
194     get toolbarItemLabel()
195     {
196         return WebInspector.UIString("Scripts");
197     },
198
199     get statusBarItems()
200     {
201         return [this.enableToggleButton.element, this._pauseOnExceptionButton.element];
202     },
203
204     get defaultFocusedElement()
205     {
206         return this.filesSelectElement;
207     },
208
209     get paused()
210     {
211         return this._paused;
212     },
213
214     show: function()
215     {
216         WebInspector.Panel.prototype.show.call(this);
217         this.sidebarResizeElement.style.right = (this.sidebarElement.offsetWidth - 3) + "px";
218
219         if (this.visibleView)
220             this.visibleView.show(this.viewsContainerElement);
221
222         if (this._attachDebuggerWhenShown) {
223             InspectorBackend.enableDebugger(false);
224             delete this._attachDebuggerWhenShown;
225         }
226     },
227
228     hide: function()
229     {
230         if (this.visibleView)
231             this.visibleView.hide();
232         WebInspector.Panel.prototype.hide.call(this);
233     },
234
235     get breakpointsActivated()
236     {
237         return this.toggleBreakpointsButton.toggled;
238     },
239
240     _parsedScriptSource: function(event)
241     {
242         var sourceID = event.data;
243         var script = WebInspector.debuggerModel.scriptForSourceID(sourceID);
244         this._addScript(script);
245     },
246
247     _failedToParseScriptSource: function(event)
248     {
249         this._addScript(event.data);
250     },
251
252     _addScript: function(script)
253     {
254         var resource = WebInspector.resourceForURL(script.sourceURL);
255         if (resource) {
256             if (resource.finished) {
257                 // Resource is finished, bind the script right away.
258                 script.resource = resource;
259                 var view = WebInspector.ResourceManager.existingResourceViewForResource(resource);
260                 if (view && view.sourceFrame)
261                     view.sourceFrame.addScript(script);
262             } else {
263                 // Resource is not finished, bind the script later.
264                 if (!resource._scriptsPendingResourceLoad) {
265                     resource._scriptsPendingResourceLoad = [];
266                     resource.addEventListener("finished", this._resourceLoadingFinished, this);
267                 }
268                 resource._scriptsPendingResourceLoad.push(script);
269             }
270         }
271         this._addScriptToFilesMenu(script);
272     },
273
274     continueToLine: function(sourceID, line)
275     {
276         WebInspector.debuggerModel.setOneTimeBreakpoint(sourceID, line);
277         if (this.paused)
278             this._togglePause();
279     },
280
281     _resourceLoadingFinished: function(e)
282     {
283         var resource = e.target;
284         for (var i = 0; i < resource._scriptsPendingResourceLoad.length; ++i) {
285             // Bind script to resource.
286             var script = resource._scriptsPendingResourceLoad[i];
287             script.resource = resource;
288
289             // Remove script from the files list.
290             script.filesSelectOption.parentElement.removeChild(script.filesSelectOption);
291         }
292         // Adding first script will add resource.
293         this._addScriptToFilesMenu(resource._scriptsPendingResourceLoad[0]);
294         delete resource._scriptsPendingResourceLoad;
295     },
296
297     canEditScripts: function()
298     {
299         return Preferences.canEditScriptSource;
300     },
301
302     editScriptSource: function(editData, commitEditingCallback, cancelEditingCallback)
303     {
304         if (!this.canEditScripts())
305             return;
306
307         // Need to clear breakpoints and re-create them later when editing source.
308         var breakpoints = WebInspector.debuggerModel.queryBreakpoints(function(b) { return b.sourceID === editData.sourceID });
309         for (var i = 0; i < breakpoints.length; ++i)
310             breakpoints[i].remove();
311
312         function mycallback(success, newBodyOrErrorMessage, callFrames)
313         {
314             if (success) {
315                 commitEditingCallback(newBodyOrErrorMessage);
316                 if (callFrames && callFrames.length)
317                     this._debuggerPaused({ data: { callFrames: callFrames } });
318             } else {
319                 if (cancelEditingCallback)
320                     cancelEditingCallback();
321                 WebInspector.log(newBodyOrErrorMessage, WebInspector.ConsoleMessage.MessageLevel.Warning);
322             }
323             for (var i = 0; i < breakpoints.length; ++i) {
324                 var breakpoint = breakpoints[i];
325                 var newLine = breakpoint.line;
326                 if (success && breakpoint.line >= editData.line)
327                     newLine += editData.linesCountToShift;
328                 WebInspector.debuggerModel.setBreakpoint(editData.sourceID, newLine, breakpoint.enabled, breakpoint.condition);
329             }
330         };
331         InspectorBackend.editScriptSource(editData.sourceID, editData.content, mycallback.bind(this));
332     },
333
334     selectedCallFrameId: function()
335     {
336         var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame;
337         if (!selectedCallFrame)
338             return null;
339         return selectedCallFrame.id;
340     },
341
342     evaluateInSelectedCallFrame: function(code, updateInterface, objectGroup, callback)
343     {
344         var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame;
345         if (!this._paused || !selectedCallFrame)
346             return;
347
348         if (typeof updateInterface === "undefined")
349             updateInterface = true;
350
351         var self = this;
352         function updatingCallbackWrapper(result)
353         {
354             callback(result);
355             if (updateInterface)
356                 self.sidebarPanes.scopechain.update(selectedCallFrame);
357         }
358         this.doEvalInCallFrame(selectedCallFrame, code, objectGroup, updatingCallbackWrapper);
359     },
360
361     doEvalInCallFrame: function(callFrame, code, objectGroup, callback)
362     {
363         function evalCallback(result)
364         {
365             if (result)
366                 callback(WebInspector.RemoteObject.fromPayload(result));
367         }
368         InjectedScriptAccess.get(callFrame.worldId).evaluateInCallFrame(callFrame.id, code, objectGroup, evalCallback);
369     },
370
371     _debuggerPaused: function(event)
372     {
373         var callFrames = event.data.callFrames;
374
375         WebInspector.debuggerModel.removeOneTimeBreakpoint();
376         this._paused = true;
377         this._waitingToPause = false;
378         this._stepping = false;
379
380         this._updateDebuggerButtons();
381
382         WebInspector.currentPanel = this;
383
384         this.sidebarPanes.callstack.update(callFrames, event.data.eventType, event.data.eventData);
385         this.sidebarPanes.callstack.selectedCallFrame = callFrames[0];
386
387         window.focus();
388         InspectorFrontendHost.bringToFront();
389     },
390
391     _debuggerResumed: function()
392     {
393         this._paused = false;
394         this._waitingToPause = false;
395         this._stepping = false;
396
397         this._clearInterface();
398     },
399
400     attachDebuggerWhenShown: function()
401     {
402         if (this.element.parentElement) {
403             InspectorBackend.enableDebugger(false);
404         } else {
405             this._attachDebuggerWhenShown = true;
406         }
407     },
408
409     debuggerWasEnabled: function()
410     {
411         if (this._debuggerEnabled)
412             return;
413
414         this._debuggerEnabled = true;
415         this.reset(true);
416     },
417
418     debuggerWasDisabled: function()
419     {
420         if (!this._debuggerEnabled)
421             return;
422
423         this._debuggerEnabled = false;
424         this.reset(true);
425     },
426
427     reset: function(preserveItems)
428     {
429         this.visibleView = null;
430
431         delete this.currentQuery;
432         this.searchCanceled();
433
434         this._debuggerResumed();
435
436         this._backForwardList = [];
437         this._currentBackForwardIndex = -1;
438         this._updateBackAndForwardButtons();
439
440         this._resourceForURLInFilesSelect = {};
441         this.filesSelectElement.removeChildren();
442         this.functionsSelectElement.removeChildren();
443         this.viewsContainerElement.removeChildren();
444
445         var scripts = WebInspector.debuggerModel.queryScripts(function(s) { return !!s.resource; });
446         for (var i = 0; i < scripts.length; ++i)
447             delete scripts[i].resource._resourcesView;
448
449         this.sidebarPanes.watchExpressions.refreshExpressions();
450         if (!preserveItems)
451             this.sidebarPanes.workers.reset();
452     },
453
454     get visibleView()
455     {
456         return this._visibleView;
457     },
458
459     set visibleView(x)
460     {
461         if (this._visibleView === x)
462             return;
463
464         if (this._visibleView)
465             this._visibleView.hide();
466
467         this._visibleView = x;
468
469         if (x)
470             x.show(this.viewsContainerElement);
471     },
472
473     viewRecreated: function(oldView, newView)
474     {
475         if (this._visibleView === oldView)
476             this._visibleView = newView;
477     },
478
479     canShowSourceLine: function(url, line)
480     {
481         if (!this._debuggerEnabled)
482             return false;
483         return !!this._scriptOrResourceForURLAndLine(url, line);
484     },
485
486     showSourceLine: function(url, line)
487     {
488         var scriptOrResource = this._scriptOrResourceForURLAndLine(url, line);
489         this._showScriptOrResource(scriptOrResource, {line: line, shouldHighlightLine: true});
490     },
491
492     _scriptOrResourceForURLAndLine: function(url, line)
493     {
494         var scripts = WebInspector.debuggerModel.scriptsForURL(url);
495         for (var i = 0; i < scripts.length; ++i) {
496             var script = scripts[i];
497             if (script.resource)
498                 return script.resource;
499             if (script.startingLine <= line && script.startingLine + script.linesCount > line)
500                 return script;
501         }
502         return null;
503     },
504
505     showView: function(view)
506     {
507         if (!view)
508             return;
509         this._showScriptOrResource(view.resource || view.script);
510     },
511
512     handleShortcut: function(event)
513     {
514         var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
515         var handler = this._shortcuts[shortcut];
516         if (handler) {
517             handler(event);
518             event.handled = true;
519         } else
520             this.sidebarPanes.callstack.handleShortcut(event);
521     },
522
523     scriptViewForScript: function(script)
524     {
525         if (!script)
526             return null;
527         if (!script._scriptView)
528             script._scriptView = new WebInspector.ScriptView(script);
529         return script._scriptView;
530     },
531
532     sourceFrameForScript: function(script)
533     {
534         var view = this.scriptViewForScript(script);
535         if (!view)
536             return null;
537
538         // Setting up the source frame requires that we be attached.
539         if (!this.element.parentNode)
540             this.attach();
541
542         view.setupSourceFrameIfNeeded();
543         return view.sourceFrame;
544     },
545
546     _sourceFrameForScriptOrResource: function(scriptOrResource)
547     {
548         if (scriptOrResource instanceof WebInspector.Resource)
549             return this._sourceFrameForResource(scriptOrResource);
550         if (scriptOrResource instanceof WebInspector.Script)
551             return this.sourceFrameForScript(scriptOrResource);
552     },
553
554     _sourceFrameForResource: function(resource)
555     {
556         var view = WebInspector.ResourceManager.resourceViewForResource(resource);
557         if (!view)
558             return null;
559
560         if (!view.setupSourceFrameIfNeeded)
561             return null;
562
563         view.setupSourceFrameIfNeeded();
564         return view.sourceFrame;
565     },
566
567     _showScriptOrResource: function(scriptOrResource, options)
568     {
569         // options = {line:, shouldHighlightLine:, fromBackForwardAction:, initialLoad:}
570         options = options || {};
571
572         if (!scriptOrResource)
573             return;
574
575         var view;
576         if (scriptOrResource instanceof WebInspector.Resource)
577             view = WebInspector.ResourceManager.resourceViewForResource(scriptOrResource);
578         else if (scriptOrResource instanceof WebInspector.Script)
579             view = this.scriptViewForScript(scriptOrResource);
580
581         if (!view)
582             return;
583
584         var url = scriptOrResource.url || scriptOrResource.sourceURL;
585         if (url && !options.initialLoad)
586             WebInspector.settings.lastViewedScriptFile = url;
587
588         if (!options.fromBackForwardAction) {
589             var oldIndex = this._currentBackForwardIndex;
590             if (oldIndex >= 0)
591                 this._backForwardList.splice(oldIndex + 1, this._backForwardList.length - oldIndex);
592
593             // Check for a previous entry of the same object in _backForwardList.
594             // If one is found, remove it and update _currentBackForwardIndex to match.
595             var previousEntryIndex = this._backForwardList.indexOf(scriptOrResource);
596             if (previousEntryIndex !== -1) {
597                 this._backForwardList.splice(previousEntryIndex, 1);
598                 --this._currentBackForwardIndex;
599             }
600
601             this._backForwardList.push(scriptOrResource);
602             ++this._currentBackForwardIndex;
603
604             this._updateBackAndForwardButtons();
605         }
606
607         this.visibleView = view;
608
609         if (options.line) {
610             if (view.revealLine)
611                 view.revealLine(options.line);
612             if (view.highlightLine && options.shouldHighlightLine)
613                 view.highlightLine(options.line);
614         }
615
616         var option;
617         if (scriptOrResource instanceof WebInspector.Script) {
618             option = scriptOrResource.filesSelectOption;
619
620             // hasn't been added yet - happens for stepping in evals,
621             // so use the force option to force the script into the menu.
622             if (!option) {
623                 this._addScriptToFilesMenu(scriptOrResource, true);
624                 option = scriptOrResource.filesSelectOption;
625             }
626
627             console.assert(option);
628         } else
629             option = scriptOrResource.filesSelectOption;
630
631         if (option)
632             this.filesSelectElement.selectedIndex = option.index;
633     },
634
635     _addScriptToFilesMenu: function(script, force)
636     {
637         if (!script.sourceURL && !force)
638             return;
639
640         if (script.resource) {
641             if (this._resourceForURLInFilesSelect[script.resource.url])
642                 return;
643             this._resourceForURLInFilesSelect[script.resource.url] = script.resource;
644         }
645
646         var displayName = script.sourceURL ? WebInspector.displayNameForURL(script.sourceURL) : WebInspector.UIString("(program)");
647
648         var select = this.filesSelectElement;
649         var option = document.createElement("option");
650         option.representedObject = script.resource || script;
651         option.url = displayName;
652         option.startingLine = script.startingLine;
653         option.text = script.resource || script.startingLine === 1 ? displayName : String.sprintf("%s:%d", displayName, script.startingLine);
654
655         function optionCompare(a, b)
656         {
657             if (a.url < b.url)
658                 return -1;
659             else if (a.url > b.url)
660                 return 1;
661
662             if (typeof a.startingLine !== "number")
663                 return -1;
664             if (typeof b.startingLine !== "number")
665                 return -1;
666             return a.startingLine - b.startingLine;
667         }
668
669         var insertionIndex = insertionIndexForObjectInListSortedByFunction(option, select.childNodes, optionCompare);
670         if (insertionIndex < 0)
671             select.appendChild(option);
672         else
673             select.insertBefore(option, select.childNodes.item(insertionIndex));
674
675         if (script.resource)
676             script.resource.filesSelectOption = option;
677         else
678             script.filesSelectOption = option;
679
680         if (select.options[select.selectedIndex] === option) {
681             // Call _showScriptOrResource if the option we just appended ended up being selected.
682             // This will happen for the first item added to the menu.
683             this._showScriptOrResource(option.representedObject, {initialLoad: true});
684         } else {
685             // If not first item, check to see if this was the last viewed
686             var url = option.representedObject.url || option.representedObject.sourceURL;
687             var lastURL = WebInspector.settings.lastViewedScriptFile;
688             if (url && url === lastURL) {
689                 // For resources containing multiple <script> tags, we first report them separately and
690                 // then glue them all together. They all share url and there is no need to show them all one
691                 // by one.
692                 var isResource = !!option.representedObject.url;
693                 if (isResource || !this.visibleView || !this.visibleView.script || this.visibleView.script.sourceURL !== url)
694                     this._showScriptOrResource(option.representedObject, {initialLoad: true});
695             }
696         }
697
698         if (script.worldType === WebInspector.Script.WorldType.EXTENSIONS_WORLD)
699             script.filesSelectOption.addStyleClass("extension-script");
700     },
701
702     _clearCurrentExecutionLine: function()
703     {
704         if (this._executionSourceFrame)
705             this._executionSourceFrame.executionLine = 0;
706         delete this._executionSourceFrame;
707     },
708
709     _callFrameSelected: function()
710     {
711         this._clearCurrentExecutionLine();
712
713         var callStackPane = this.sidebarPanes.callstack;
714         var currentFrame = callStackPane.selectedCallFrame;
715         if (!currentFrame)
716             return;
717
718         this.sidebarPanes.scopechain.update(currentFrame);
719         this.sidebarPanes.watchExpressions.refreshExpressions();
720
721         var script = WebInspector.debuggerModel.scriptForSourceID(currentFrame.sourceID);
722         var scriptOrResource = script.resource || script;
723         this._showScriptOrResource(scriptOrResource, {line: currentFrame.line});
724
725         this._executionSourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource);
726         if (this._executionSourceFrame)
727             this._executionSourceFrame.executionLine = currentFrame.line;
728     },
729
730     _changeVisibleFile: function(event)
731     {
732         var select = this.filesSelectElement;
733         this._showScriptOrResource(select.options[select.selectedIndex].representedObject);
734     },
735
736     _startSidebarResizeDrag: function(event)
737     {
738         WebInspector.elementDragStart(this.sidebarElement, this._sidebarResizeDrag.bind(this), this._endSidebarResizeDrag.bind(this), event, "col-resize");
739
740         if (event.target === this.sidebarResizeWidgetElement)
741             this._dragOffset = (event.target.offsetWidth - (event.pageX - event.target.totalOffsetLeft));
742         else
743             this._dragOffset = 0;
744     },
745
746     _endSidebarResizeDrag: function(event)
747     {
748         WebInspector.elementDragEnd(event);
749         delete this._dragOffset;
750         this.saveSidebarWidth();
751     },
752
753     _sidebarResizeDrag: function(event)
754     {
755         var x = event.pageX + this._dragOffset;
756         var newWidth = Number.constrain(window.innerWidth - x, Preferences.minScriptsSidebarWidth, window.innerWidth * 0.66);
757         this.setSidebarWidth(newWidth);
758         event.preventDefault();
759     },
760
761     setSidebarWidth: function(newWidth)
762     {
763         this.sidebarElement.style.width = newWidth + "px";
764         this.sidebarButtonsElement.style.width = newWidth + "px";
765         this.viewsContainerElement.style.right = newWidth + "px";
766         this.sidebarResizeWidgetElement.style.right = newWidth + "px";
767         this.sidebarResizeElement.style.right = (newWidth - 3) + "px";
768
769         this.resize();
770     },
771
772     updatePauseOnExceptionsState: function(pauseOnExceptionsState)
773     {
774         if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions)
775             this._pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions.");
776         else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnAllExceptions)
777             this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on all exceptions.\nClick to Pause on uncaught exceptions.");
778         else if (pauseOnExceptionsState == WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnUncaughtExceptions)
779             this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on uncaught exceptions.\nClick to Not pause on exceptions.");
780
781         this._pauseOnExceptionButton.state = pauseOnExceptionsState;
782     },
783
784     _updateDebuggerButtons: function()
785     {
786         if (this._debuggerEnabled) {
787             this.enableToggleButton.title = WebInspector.UIString("Debugging enabled. Click to disable.");
788             this.enableToggleButton.toggled = true;
789             this._pauseOnExceptionButton.visible = true;
790             this.panelEnablerView.visible = false;
791         } else {
792             this.enableToggleButton.title = WebInspector.UIString("Debugging disabled. Click to enable.");
793             this.enableToggleButton.toggled = false;
794             this._pauseOnExceptionButton.visible = false;
795             this.panelEnablerView.visible = true;
796         }
797
798         if (this._paused) {
799             this.pauseButton.addStyleClass("paused");
800
801             this.pauseButton.disabled = false;
802             this.stepOverButton.disabled = false;
803             this.stepIntoButton.disabled = false;
804             this.stepOutButton.disabled = false;
805
806             this.debuggerStatusElement.textContent = WebInspector.UIString("Paused");
807         } else {
808             this.pauseButton.removeStyleClass("paused");
809
810             this.pauseButton.disabled = this._waitingToPause;
811             this.stepOverButton.disabled = true;
812             this.stepIntoButton.disabled = true;
813             this.stepOutButton.disabled = true;
814
815             if (this._waitingToPause)
816                 this.debuggerStatusElement.textContent = WebInspector.UIString("Pausing");
817             else if (this._stepping)
818                 this.debuggerStatusElement.textContent = WebInspector.UIString("Stepping");
819             else
820                 this.debuggerStatusElement.textContent = "";
821         }
822     },
823
824     _updateBackAndForwardButtons: function()
825     {
826         this.backButton.disabled = this._currentBackForwardIndex <= 0;
827         this.forwardButton.disabled = this._currentBackForwardIndex >= (this._backForwardList.length - 1);
828     },
829
830     _clearInterface: function()
831     {
832         this.sidebarPanes.callstack.update(null);
833         this.sidebarPanes.scopechain.update(null);
834
835         this._clearCurrentExecutionLine();
836         this._updateDebuggerButtons();
837     },
838
839     _goBack: function()
840     {
841         if (this._currentBackForwardIndex <= 0) {
842             console.error("Can't go back from index " + this._currentBackForwardIndex);
843             return;
844         }
845
846         this._showScriptOrResource(this._backForwardList[--this._currentBackForwardIndex], {fromBackForwardAction: true});
847         this._updateBackAndForwardButtons();
848     },
849
850     _goForward: function()
851     {
852         if (this._currentBackForwardIndex >= this._backForwardList.length - 1) {
853             console.error("Can't go forward from index " + this._currentBackForwardIndex);
854             return;
855         }
856
857         this._showScriptOrResource(this._backForwardList[++this._currentBackForwardIndex], {fromBackForwardAction: true});
858         this._updateBackAndForwardButtons();
859     },
860
861     _enableDebugging: function()
862     {
863         if (this._debuggerEnabled)
864             return;
865         this._toggleDebugging(this.panelEnablerView.alwaysEnabled);
866     },
867
868     _toggleDebugging: function(optionalAlways)
869     {
870         this._paused = false;
871         this._waitingToPause = false;
872         this._stepping = false;
873
874         if (this._debuggerEnabled)
875             InspectorBackend.disableDebugger(true);
876         else
877             InspectorBackend.enableDebugger(!!optionalAlways);
878     },
879
880     _togglePauseOnExceptions: function()
881     {
882         InspectorBackend.setPauseOnExceptionsState((this._pauseOnExceptionButton.state + 1) % this._pauseOnExceptionButton.states, this.updatePauseOnExceptionsState.bind(this));
883     },
884
885     _togglePause: function()
886     {
887         if (this._paused) {
888             this._paused = false;
889             this._waitingToPause = false;
890             InspectorBackend.resume();
891         } else {
892             this._stepping = false;
893             this._waitingToPause = true;
894             InspectorBackend.pause();
895         }
896
897         this._clearInterface();
898     },
899
900     _stepOverClicked: function()
901     {
902         this._paused = false;
903         this._stepping = true;
904
905         this._clearInterface();
906
907         InspectorBackend.stepOverStatement();
908     },
909
910     _stepIntoClicked: function()
911     {
912         this._paused = false;
913         this._stepping = true;
914
915         this._clearInterface();
916
917         InspectorBackend.stepIntoStatement();
918     },
919
920     _stepOutClicked: function()
921     {
922         this._paused = false;
923         this._stepping = true;
924
925         this._clearInterface();
926
927         InspectorBackend.stepOutOfFunction();
928     },
929
930     toggleBreakpointsClicked: function()
931     {
932         this.toggleBreakpointsButton.toggled = !this.toggleBreakpointsButton.toggled;
933         if (this.toggleBreakpointsButton.toggled) {
934             InspectorBackend.activateBreakpoints();
935             this.toggleBreakpointsButton.title = WebInspector.UIString("Deactivate all breakpoints.");
936             document.getElementById("main-panels").removeStyleClass("breakpoints-deactivated");
937         } else {
938             InspectorBackend.deactivateBreakpoints();
939             this.toggleBreakpointsButton.title = WebInspector.UIString("Activate all breakpoints.");
940             document.getElementById("main-panels").addStyleClass("breakpoints-deactivated");
941         }
942     },
943
944     elementsToRestoreScrollPositionsFor: function()
945     {
946         return [ this.sidebarElement ];
947     },
948
949     _registerShortcuts: function()
950     {
951         var section = WebInspector.shortcutsHelp.section(WebInspector.UIString("Scripts Panel"));
952         var handler, shortcut1, shortcut2;
953         var platformSpecificModifier = WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta;
954
955         this._shortcuts = {};
956
957         // Continue.
958         handler = this.pauseButton.click.bind(this.pauseButton);
959         shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F8);
960         this._shortcuts[shortcut1.key] = handler;
961         shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Slash, platformSpecificModifier);
962         this._shortcuts[shortcut2.key] = handler;
963         section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Continue"));
964
965         // Step over.
966         handler = this.stepOverButton.click.bind(this.stepOverButton);
967         shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F10);
968         this._shortcuts[shortcut1.key] = handler;
969         shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.SingleQuote, platformSpecificModifier);
970         this._shortcuts[shortcut2.key] = handler;
971         section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Step over"));
972
973         // Step into.
974         handler = this.stepIntoButton.click.bind(this.stepIntoButton);
975         shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F11);
976         this._shortcuts[shortcut1.key] = handler;
977         shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Semicolon, platformSpecificModifier);
978         this._shortcuts[shortcut2.key] = handler;
979         section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Step into"));
980
981         // Step out.
982         handler = this.stepOutButton.click.bind(this.stepOutButton);
983         shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.F11, WebInspector.KeyboardShortcut.Modifiers.Shift);
984         this._shortcuts[shortcut1.key] = handler;
985         shortcut2 = WebInspector.KeyboardShortcut.makeDescriptor(WebInspector.KeyboardShortcut.Keys.Semicolon, WebInspector.KeyboardShortcut.Modifiers.Shift, platformSpecificModifier);
986         this._shortcuts[shortcut2.key] = handler;
987         section.addAlternateKeys([ shortcut1.name, shortcut2.name ], WebInspector.UIString("Step out"));
988
989         var isMac = WebInspector.isMac();
990         if (isMac)
991             shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor("l", WebInspector.KeyboardShortcut.Modifiers.Meta);
992         else
993             shortcut1 = WebInspector.KeyboardShortcut.makeDescriptor("g", WebInspector.KeyboardShortcut.Modifiers.Ctrl);
994         this._shortcuts[shortcut1.key] = this.showGoToLineDialog.bind(this);
995         section.addAlternateKeys([ shortcut1.name ], WebInspector.UIString("Go to Line"));
996         this.sidebarPanes.callstack.registerShortcuts(section);
997     },
998
999     searchCanceled: function()
1000     {
1001         WebInspector.updateSearchMatchesCount(0, this);
1002
1003         if (this._searchView)
1004             this._searchView.searchCanceled();
1005
1006         delete this._searchView;
1007         delete this._searchQuery;
1008     },
1009
1010     performSearch: function(query)
1011     {
1012         if (!this.visibleView)
1013             return;
1014
1015         // Call searchCanceled since it will reset everything we need before doing a new search.
1016         this.searchCanceled();
1017
1018         this._searchView = this.visibleView;
1019         this._searchQuery = query;
1020
1021         function finishedCallback(view, searchMatches)
1022         {
1023             if (!searchMatches)
1024                 return;
1025
1026             WebInspector.updateSearchMatchesCount(searchMatches, this);
1027             view.jumpToFirstSearchResult();
1028         }
1029
1030         this._searchView.performSearch(query, finishedCallback.bind(this));
1031     },
1032
1033     jumpToNextSearchResult: function()
1034     {
1035         if (!this._searchView)
1036             return;
1037
1038         if (this._searchView !== this.visibleView) {
1039             this.performSearch(this._searchQuery);
1040             return;
1041         }
1042
1043         if (this._searchView.showingLastSearchResult())
1044             this._searchView.jumpToFirstSearchResult();
1045         else
1046             this._searchView.jumpToNextSearchResult();
1047     },
1048
1049     jumpToPreviousSearchResult: function()
1050     {
1051         if (!this._searchView)
1052             return;
1053
1054         if (this._searchView !== this.visibleView) {
1055             this.performSearch(this._searchQuery);
1056             if (this._searchView)
1057                 this._searchView.jumpToLastSearchResult();
1058             return;
1059         }
1060
1061         if (this._searchView.showingFirstSearchResult())
1062             this._searchView.jumpToLastSearchResult();
1063         else
1064             this._searchView.jumpToPreviousSearchResult();
1065     },
1066
1067     showGoToLineDialog: function(e)
1068     {
1069          var view = this.visibleView;
1070          if (view)
1071              WebInspector.GoToLineDialog.show(view);
1072     }
1073 }
1074
1075 WebInspector.ScriptsPanel.prototype.__proto__ = WebInspector.Panel.prototype;