• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
26WebInspector.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.backButton.addEventListener("click", this._goBack.bind(this), false);
44    this.topStatusBar.appendChild(this.backButton);
45
46    this.forwardButton = document.createElement("button");
47    this.forwardButton.className = "status-bar-item";
48    this.forwardButton.id = "scripts-forward";
49    this.forwardButton.title = WebInspector.UIString("Show the next script resource.");
50    this.forwardButton.disabled = true;
51    this.forwardButton.appendChild(document.createElement("img"));
52    this.forwardButton.addEventListener("click", this._goForward.bind(this), false);
53    this.topStatusBar.appendChild(this.forwardButton);
54
55    this.filesSelectElement = document.createElement("select");
56    this.filesSelectElement.className = "status-bar-item";
57    this.filesSelectElement.id = "scripts-files";
58    this.filesSelectElement.addEventListener("change", this._changeVisibleFile.bind(this), false);
59    this.filesSelectElement.handleKeyEvent = this.handleKeyEvent.bind(this);
60    this.topStatusBar.appendChild(this.filesSelectElement);
61
62    this.functionsSelectElement = document.createElement("select");
63    this.functionsSelectElement.className = "status-bar-item";
64    this.functionsSelectElement.id = "scripts-functions";
65
66    // FIXME: append the functions select element to the top status bar when it is implemented.
67    // this.topStatusBar.appendChild(this.functionsSelectElement);
68
69    this.sidebarButtonsElement = document.createElement("div");
70    this.sidebarButtonsElement.id = "scripts-sidebar-buttons";
71    this.topStatusBar.appendChild(this.sidebarButtonsElement);
72
73    this.pauseButton = document.createElement("button");
74    this.pauseButton.className = "status-bar-item";
75    this.pauseButton.id = "scripts-pause";
76    this.pauseButton.title = WebInspector.UIString("Pause script execution.");
77    this.pauseButton.disabled = true;
78    this.pauseButton.appendChild(document.createElement("img"));
79    this.pauseButton.addEventListener("click", this._togglePause.bind(this), false);
80    this.sidebarButtonsElement.appendChild(this.pauseButton);
81
82    this.stepOverButton = document.createElement("button");
83    this.stepOverButton.className = "status-bar-item";
84    this.stepOverButton.id = "scripts-step-over";
85    this.stepOverButton.title = WebInspector.UIString("Step over next function call.");
86    this.stepOverButton.disabled = true;
87    this.stepOverButton.addEventListener("click", this._stepOverClicked.bind(this), false);
88    this.stepOverButton.appendChild(document.createElement("img"));
89    this.sidebarButtonsElement.appendChild(this.stepOverButton);
90
91    this.stepIntoButton = document.createElement("button");
92    this.stepIntoButton.className = "status-bar-item";
93    this.stepIntoButton.id = "scripts-step-into";
94    this.stepIntoButton.title = WebInspector.UIString("Step into next function call.");
95    this.stepIntoButton.disabled = true;
96    this.stepIntoButton.addEventListener("click", this._stepIntoClicked.bind(this), false);
97    this.stepIntoButton.appendChild(document.createElement("img"));
98    this.sidebarButtonsElement.appendChild(this.stepIntoButton);
99
100    this.stepOutButton = document.createElement("button");
101    this.stepOutButton.className = "status-bar-item";
102    this.stepOutButton.id = "scripts-step-out";
103    this.stepOutButton.title = WebInspector.UIString("Step out of current function.");
104    this.stepOutButton.disabled = true;
105    this.stepOutButton.addEventListener("click", this._stepOutClicked.bind(this), false);
106    this.stepOutButton.appendChild(document.createElement("img"));
107    this.sidebarButtonsElement.appendChild(this.stepOutButton);
108
109    this.debuggerStatusElement = document.createElement("div");
110    this.debuggerStatusElement.id = "scripts-debugger-status";
111    this.sidebarButtonsElement.appendChild(this.debuggerStatusElement);
112
113    this.viewsContainerElement = document.createElement("div");
114    this.viewsContainerElement.id = "script-resource-views";
115
116    this.sidebarElement = document.createElement("div");
117    this.sidebarElement.id = "scripts-sidebar";
118
119    this.sidebarResizeElement = document.createElement("div");
120    this.sidebarResizeElement.className = "sidebar-resizer-vertical";
121    this.sidebarResizeElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false);
122
123    this.sidebarResizeWidgetElement = document.createElement("div");
124    this.sidebarResizeWidgetElement.id = "scripts-sidebar-resizer-widget";
125    this.sidebarResizeWidgetElement.addEventListener("mousedown", this._startSidebarResizeDrag.bind(this), false);
126    this.topStatusBar.appendChild(this.sidebarResizeWidgetElement);
127
128    this.sidebarPanes = {};
129    this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane();
130    this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane();
131    this.sidebarPanes.breakpoints = new WebInspector.BreakpointsSidebarPane();
132
133    for (var pane in this.sidebarPanes)
134        this.sidebarElement.appendChild(this.sidebarPanes[pane].element);
135
136    this.sidebarPanes.callstack.expanded = true;
137    this.sidebarPanes.callstack.addEventListener("call frame selected", this._callFrameSelected, this);
138
139    this.sidebarPanes.scopechain.expanded = true;
140    this.sidebarPanes.breakpoints.expanded = true;
141
142    var panelEnablerHeading = WebInspector.UIString("You need to enable debugging before you can use the Scripts panel.");
143    var panelEnablerDisclaimer = WebInspector.UIString("Enabling debugging will make scripts run slower.");
144    var panelEnablerButton = WebInspector.UIString("Enable Debugging");
145
146    this.panelEnablerView = new WebInspector.PanelEnablerView("scripts", panelEnablerHeading, panelEnablerDisclaimer, panelEnablerButton);
147    this.panelEnablerView.addEventListener("enable clicked", this._enableDebugging, this);
148
149    this.element.appendChild(this.panelEnablerView.element);
150    this.element.appendChild(this.viewsContainerElement);
151    this.element.appendChild(this.sidebarElement);
152    this.element.appendChild(this.sidebarResizeElement);
153
154    this.enableToggleButton = this.createStatusBarButton();
155    this.enableToggleButton.className = "enable-toggle-status-bar-item status-bar-item";
156    this.enableToggleButton.addEventListener("click", this._toggleDebugging.bind(this), false);
157
158    this.pauseOnExceptionButton = this.createStatusBarButton();
159    this.pauseOnExceptionButton.id = "scripts-pause-on-exceptions-status-bar-item";
160    this.pauseOnExceptionButton.className = "status-bar-item";
161    this.pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions.bind(this), false);
162
163    this._breakpointsURLMap = {};
164
165    this._shortcuts = {};
166
167    var isMac = InspectorController.platform().indexOf("mac-") === 0;
168    var platformSpecificModifier = isMac ? WebInspector.KeyboardShortcut.Modifiers.Meta : WebInspector.KeyboardShortcut.Modifiers.Ctrl;
169
170    // Continue.
171    var handler = this.pauseButton.click.bind(this.pauseButton);
172    var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.F8);
173    this._shortcuts[shortcut] = handler;
174    var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.Slash, platformSpecificModifier);
175    this._shortcuts[shortcut] = handler;
176
177    // Step over.
178    var handler = this.stepOverButton.click.bind(this.stepOverButton);
179    var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.F10);
180    this._shortcuts[shortcut] = handler;
181    var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.SingleQuote, platformSpecificModifier);
182    this._shortcuts[shortcut] = handler;
183
184    // Step into.
185    var handler = this.stepIntoButton.click.bind(this.stepIntoButton);
186    var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.F11);
187    this._shortcuts[shortcut] = handler;
188    var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.Semicolon, platformSpecificModifier);
189    this._shortcuts[shortcut] = handler;
190
191    // Step out.
192    var handler = this.stepOutButton.click.bind(this.stepOutButton);
193    var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.F11, WebInspector.KeyboardShortcut.Modifiers.Shift);
194    this._shortcuts[shortcut] = handler;
195    var shortcut = WebInspector.KeyboardShortcut.makeKey(WebInspector.KeyboardShortcut.KeyCodes.Semicolon, WebInspector.KeyboardShortcut.Modifiers.Shift, platformSpecificModifier);
196    this._shortcuts[shortcut] = handler;
197
198    this.reset();
199}
200
201WebInspector.ScriptsPanel.prototype = {
202    toolbarItemClass: "scripts",
203
204    get toolbarItemLabel()
205    {
206        return WebInspector.UIString("Scripts");
207    },
208
209    get statusBarItems()
210    {
211        return [this.enableToggleButton, this.pauseOnExceptionButton];
212    },
213
214    get paused()
215    {
216        return this._paused;
217    },
218
219    show: function()
220    {
221        WebInspector.Panel.prototype.show.call(this);
222        this.sidebarResizeElement.style.right = (this.sidebarElement.offsetWidth - 3) + "px";
223
224        if (this.visibleView) {
225            if (this.visibleView instanceof WebInspector.ResourceView)
226                this.visibleView.headersVisible = false;
227            this.visibleView.show(this.viewsContainerElement);
228        }
229
230        // Hide any views that are visible that are not this panel's current visible view.
231        // This can happen when a ResourceView is visible in the Resources panel then switched
232        // to the this panel.
233        for (var sourceID in this._sourceIDMap) {
234            var scriptOrResource = this._sourceIDMap[sourceID];
235            var view = this._sourceViewForScriptOrResource(scriptOrResource);
236            if (!view || view === this.visibleView)
237                continue;
238            view.visible = false;
239        }
240        if (this._attachDebuggerWhenShown) {
241            InspectorController.enableDebugger(false);
242            delete this._attachDebuggerWhenShown;
243        }
244    },
245
246    get searchableViews()
247    {
248        var views = [];
249
250        const visibleView = this.visibleView;
251        if (visibleView && visibleView.performSearch) {
252            visibleView.alreadySearching = true;
253            views.push(visibleView);
254        }
255
256        for (var sourceID in this._sourceIDMap) {
257            var scriptOrResource = this._sourceIDMap[sourceID];
258            var view = this._sourceViewForScriptOrResource(scriptOrResource);
259            if (!view || !view.performSearch || view.alreadySearching)
260                continue;
261
262            view.alreadySearching = true;
263            views.push(view);
264        }
265
266        for (var i = 0; i < views.length; ++i)
267            delete views[i].alreadySearching;
268
269        return views;
270    },
271
272    addScript: function(sourceID, sourceURL, source, startingLine, errorLine, errorMessage)
273    {
274        var script = new WebInspector.Script(sourceID, sourceURL, source, startingLine, errorLine, errorMessage);
275
276        if (sourceURL in WebInspector.resourceURLMap) {
277            var resource = WebInspector.resourceURLMap[sourceURL];
278            resource.addScript(script);
279        }
280
281        if (sourceURL in this._breakpointsURLMap && sourceID) {
282            var breakpoints = this._breakpointsURLMap[sourceURL];
283            var breakpointsLength = breakpoints.length;
284            for (var i = 0; i < breakpointsLength; ++i) {
285                var breakpoint = breakpoints[i];
286                if (startingLine <= breakpoint.line) {
287                    breakpoint.sourceID = sourceID;
288                    if (breakpoint.enabled)
289                        InspectorController.addBreakpoint(breakpoint.sourceID, breakpoint.line);
290                }
291            }
292        }
293
294        if (sourceID)
295            this._sourceIDMap[sourceID] = (resource || script);
296
297        this._addScriptToFilesMenu(script);
298    },
299
300    scriptOrResourceForID: function(id)
301    {
302        return this._sourceIDMap[id];
303    },
304
305    addBreakpoint: function(breakpoint)
306    {
307        this.sidebarPanes.breakpoints.addBreakpoint(breakpoint);
308
309        var sourceFrame;
310        if (breakpoint.url) {
311            if (!(breakpoint.url in this._breakpointsURLMap))
312                this._breakpointsURLMap[breakpoint.url] = [];
313            this._breakpointsURLMap[breakpoint.url].unshift(breakpoint);
314
315            if (breakpoint.url in WebInspector.resourceURLMap) {
316                var resource = WebInspector.resourceURLMap[breakpoint.url];
317                sourceFrame = this._sourceFrameForScriptOrResource(resource);
318            }
319        }
320
321        if (breakpoint.sourceID && !sourceFrame) {
322            var object = this._sourceIDMap[breakpoint.sourceID]
323            sourceFrame = this._sourceFrameForScriptOrResource(object);
324        }
325
326        if (sourceFrame)
327            sourceFrame.addBreakpoint(breakpoint);
328    },
329
330    removeBreakpoint: function(breakpoint)
331    {
332        this.sidebarPanes.breakpoints.removeBreakpoint(breakpoint);
333
334        var sourceFrame;
335        if (breakpoint.url && breakpoint.url in this._breakpointsURLMap) {
336            var breakpoints = this._breakpointsURLMap[breakpoint.url];
337            breakpoints.remove(breakpoint);
338            if (!breakpoints.length)
339                delete this._breakpointsURLMap[breakpoint.url];
340
341            if (breakpoint.url in WebInspector.resourceURLMap) {
342                var resource = WebInspector.resourceURLMap[breakpoint.url];
343                sourceFrame = this._sourceFrameForScriptOrResource(resource);
344            }
345        }
346
347        if (breakpoint.sourceID && !sourceFrame) {
348            var object = this._sourceIDMap[breakpoint.sourceID]
349            sourceFrame = this._sourceFrameForScriptOrResource(object);
350        }
351
352        if (sourceFrame)
353            sourceFrame.removeBreakpoint(breakpoint);
354    },
355
356    evaluateInSelectedCallFrame: function(code, updateInterface, callback)
357    {
358        var selectedCallFrame = this.sidebarPanes.callstack.selectedCallFrame;
359        if (!this._paused || !selectedCallFrame)
360            return;
361
362        if (typeof updateInterface === "undefined")
363            updateInterface = true;
364
365        var self = this;
366        function updatingCallbackWrapper(result, exception)
367        {
368            callback(result, exception);
369            if (updateInterface)
370                self.sidebarPanes.scopechain.update(selectedCallFrame);
371        }
372        this.doEvalInCallFrame(selectedCallFrame, code, updatingCallbackWrapper);
373    },
374
375    doEvalInCallFrame: function(callFrame, code, callback)
376    {
377        var panel = this;
378        function delayedEvaluation()
379        {
380            if (!code) {
381                // Evaluate into properties in scope of the selected call frame.
382                callback(panel._variablesInScope(callFrame));
383                return;
384            }
385            try {
386                callback(callFrame.evaluate(code));
387            } catch (e) {
388                callback(e, true);
389            }
390        }
391        setTimeout(delayedEvaluation, 0);
392    },
393
394    _variablesInScope: function(callFrame)
395    {
396        var result = {};
397        var scopeChain = callFrame.scopeChain;
398        for (var i = 0; i < scopeChain.length; ++i) {
399            var scopeObject = scopeChain[i];
400            for (var property in scopeObject)
401                result[property] = true;
402        }
403        return result;
404    },
405
406    debuggerPaused: function()
407    {
408        this._paused = true;
409        this._waitingToPause = false;
410        this._stepping = false;
411
412        this._updateDebuggerButtons();
413
414        var callStackPane = this.sidebarPanes.callstack;
415        var currentFrame = InspectorController.currentCallFrame();
416        callStackPane.update(currentFrame, this._sourceIDMap);
417        callStackPane.selectedCallFrame = currentFrame;
418
419        WebInspector.currentPanel = this;
420        window.focus();
421    },
422
423    debuggerResumed: function()
424    {
425        this._paused = false;
426        this._waitingToPause = false;
427        this._stepping = false;
428
429        this._clearInterface();
430    },
431
432    attachDebuggerWhenShown: function()
433    {
434        if (this.element.parentElement) {
435            InspectorController.enableDebugger(false);
436        } else {
437            this._attachDebuggerWhenShown = true;
438        }
439    },
440
441    debuggerWasEnabled: function()
442    {
443        this.reset();
444    },
445
446    debuggerWasDisabled: function()
447    {
448        this.reset();
449    },
450
451    reset: function()
452    {
453        this.visibleView = null;
454
455        delete this.currentQuery;
456        this.searchCanceled();
457
458        if (!InspectorController.debuggerEnabled()) {
459            this._paused = false;
460            this._waitingToPause = false;
461            this._stepping = false;
462        }
463
464        this._clearInterface();
465
466        this._backForwardList = [];
467        this._currentBackForwardIndex = -1;
468        this._updateBackAndForwardButtons();
469
470        this._scriptsForURLsInFilesSelect = {};
471        this.filesSelectElement.removeChildren();
472        this.functionsSelectElement.removeChildren();
473        this.viewsContainerElement.removeChildren();
474
475        if (this._sourceIDMap) {
476            for (var sourceID in this._sourceIDMap) {
477                var object = this._sourceIDMap[sourceID];
478                if (object instanceof WebInspector.Resource)
479                    object.removeAllScripts();
480            }
481        }
482
483        this._sourceIDMap = {};
484    },
485
486    get visibleView()
487    {
488        return this._visibleView;
489    },
490
491    set visibleView(x)
492    {
493        if (this._visibleView === x)
494            return;
495
496        if (this._visibleView)
497            this._visibleView.hide();
498
499        this._visibleView = x;
500
501        if (x)
502            x.show(this.viewsContainerElement);
503    },
504
505    canShowResource: function(resource)
506    {
507        return resource && resource.scripts.length && InspectorController.debuggerEnabled();
508    },
509
510    showScript: function(script, line)
511    {
512        this._showScriptOrResource(script, line, true);
513    },
514
515    showResource: function(resource, line)
516    {
517        this._showScriptOrResource(resource, line, true);
518    },
519
520    showView: function(view)
521    {
522        if (!view)
523            return;
524        this._showScriptOrResource((view.resource || view.script));
525    },
526
527    handleKeyEvent: function(event)
528    {
529        var shortcut = WebInspector.KeyboardShortcut.makeKeyFromEvent(event);
530        var handler = this._shortcuts[shortcut];
531        if (handler) {
532            handler(event);
533            event.preventDefault();
534            event.handled = true;
535        } else {
536            this.sidebarPanes.callstack.handleKeyEvent(event);
537        }
538    },
539
540    scriptViewForScript: function(script)
541    {
542        if (!script)
543            return null;
544        if (!script._scriptView)
545            script._scriptView = new WebInspector.ScriptView(script);
546        return script._scriptView;
547    },
548
549    sourceFrameForScript: function(script)
550    {
551        var view = this.scriptViewForScript(script);
552        if (!view)
553            return null;
554
555        // Setting up the source frame requires that we be attached.
556        if (!this.element.parentNode)
557            this.attach();
558
559        view.setupSourceFrameIfNeeded();
560        return view.sourceFrame;
561    },
562
563    _sourceViewForScriptOrResource: function(scriptOrResource)
564    {
565        if (scriptOrResource instanceof WebInspector.Resource) {
566            if (!WebInspector.panels.resources)
567                return null;
568            return WebInspector.panels.resources.resourceViewForResource(scriptOrResource);
569        }
570        if (scriptOrResource instanceof WebInspector.Script)
571            return this.scriptViewForScript(scriptOrResource);
572    },
573
574    _sourceFrameForScriptOrResource: function(scriptOrResource)
575    {
576        if (scriptOrResource instanceof WebInspector.Resource) {
577            if (!WebInspector.panels.resources)
578                return null;
579            return WebInspector.panels.resources.sourceFrameForResource(scriptOrResource);
580        }
581        if (scriptOrResource instanceof WebInspector.Script)
582            return this.sourceFrameForScript(scriptOrResource);
583    },
584
585    _showScriptOrResource: function(scriptOrResource, line, shouldHighlightLine, fromBackForwardAction)
586    {
587        if (!scriptOrResource)
588            return;
589
590        var view;
591        if (scriptOrResource instanceof WebInspector.Resource) {
592            if (!WebInspector.panels.resources)
593                return null;
594            view = WebInspector.panels.resources.resourceViewForResource(scriptOrResource);
595            view.headersVisible = false;
596
597            if (scriptOrResource.url in this._breakpointsURLMap) {
598                var sourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource);
599                if (sourceFrame && !sourceFrame.breakpoints.length) {
600                    var breakpoints = this._breakpointsURLMap[scriptOrResource.url];
601                    var breakpointsLength = breakpoints.length;
602                    for (var i = 0; i < breakpointsLength; ++i)
603                        sourceFrame.addBreakpoint(breakpoints[i]);
604                }
605            }
606        } else if (scriptOrResource instanceof WebInspector.Script)
607            view = this.scriptViewForScript(scriptOrResource);
608
609        if (!view)
610            return;
611
612        if (!fromBackForwardAction) {
613            var oldIndex = this._currentBackForwardIndex;
614            if (oldIndex >= 0)
615                this._backForwardList.splice(oldIndex + 1, this._backForwardList.length - oldIndex);
616
617            // Check for a previous entry of the same object in _backForwardList.
618            // If one is found, remove it and update _currentBackForwardIndex to match.
619            var previousEntryIndex = this._backForwardList.indexOf(scriptOrResource);
620            if (previousEntryIndex !== -1) {
621                this._backForwardList.splice(previousEntryIndex, 1);
622                --this._currentBackForwardIndex;
623            }
624
625            this._backForwardList.push(scriptOrResource);
626            ++this._currentBackForwardIndex;
627
628            this._updateBackAndForwardButtons();
629        }
630
631        this.visibleView = view;
632
633        if (line) {
634            if (view.revealLine)
635                view.revealLine(line);
636            if (view.highlightLine && shouldHighlightLine)
637                view.highlightLine(line);
638        }
639
640        var option;
641        if (scriptOrResource instanceof WebInspector.Script) {
642            option = scriptOrResource.filesSelectOption;
643            console.assert(option);
644        } else {
645            var url = scriptOrResource.url;
646            var script = this._scriptsForURLsInFilesSelect[url];
647            if (script)
648               option = script.filesSelectOption;
649        }
650
651        if (option)
652            this.filesSelectElement.selectedIndex = option.index;
653    },
654
655    _addScriptToFilesMenu: function(script)
656    {
657        if (script.resource && this._scriptsForURLsInFilesSelect[script.sourceURL])
658            return;
659
660        this._scriptsForURLsInFilesSelect[script.sourceURL] = script;
661
662        var select = this.filesSelectElement;
663
664        var option = document.createElement("option");
665        option.representedObject = (script.resource || script);
666        option.text = (script.sourceURL ? WebInspector.displayNameForURL(script.sourceURL) : WebInspector.UIString("(program)"));
667
668        function optionCompare(a, b)
669        {
670            var aTitle = a.text.toLowerCase();
671            var bTitle = b.text.toLowerCase();
672            if (aTitle < bTitle)
673                return -1;
674            else if (aTitle > bTitle)
675                return 1;
676
677            var aSourceID = a.representedObject.sourceID;
678            var bSourceID = b.representedObject.sourceID;
679            if (aSourceID < bSourceID)
680                return -1;
681            else if (aSourceID > bSourceID)
682                return 1;
683            return 0;
684        }
685
686        var insertionIndex = insertionIndexForObjectInListSortedByFunction(option, select.childNodes, optionCompare);
687        if (insertionIndex < 0)
688            select.appendChild(option);
689        else
690            select.insertBefore(option, select.childNodes.item(insertionIndex));
691
692        script.filesSelectOption = option;
693
694        // Call _showScriptOrResource if the option we just appended ended up being selected.
695        // This will happen for the first item added to the menu.
696        if (select.options[select.selectedIndex] === option)
697            this._showScriptOrResource(option.representedObject);
698    },
699
700    _clearCurrentExecutionLine: function()
701    {
702        if (this._executionSourceFrame)
703            this._executionSourceFrame.executionLine = 0;
704        delete this._executionSourceFrame;
705    },
706
707    _callFrameSelected: function()
708    {
709        this._clearCurrentExecutionLine();
710
711        var callStackPane = this.sidebarPanes.callstack;
712        var currentFrame = callStackPane.selectedCallFrame;
713        if (!currentFrame)
714            return;
715
716        this.sidebarPanes.scopechain.update(currentFrame);
717
718        var scriptOrResource = this._sourceIDMap[currentFrame.sourceID];
719        this._showScriptOrResource(scriptOrResource, currentFrame.line);
720
721        this._executionSourceFrame = this._sourceFrameForScriptOrResource(scriptOrResource);
722        if (this._executionSourceFrame)
723            this._executionSourceFrame.executionLine = currentFrame.line;
724    },
725
726    _changeVisibleFile: function(event)
727    {
728        var select = this.filesSelectElement;
729        this._showScriptOrResource(select.options[select.selectedIndex].representedObject);
730    },
731
732    _startSidebarResizeDrag: function(event)
733    {
734        WebInspector.elementDragStart(this.sidebarElement, this._sidebarResizeDrag.bind(this), this._endSidebarResizeDrag.bind(this), event, "col-resize");
735
736        if (event.target === this.sidebarResizeWidgetElement)
737            this._dragOffset = (event.target.offsetWidth - (event.pageX - event.target.totalOffsetLeft));
738        else
739            this._dragOffset = 0;
740    },
741
742    _endSidebarResizeDrag: function(event)
743    {
744        WebInspector.elementDragEnd(event);
745
746        delete this._dragOffset;
747    },
748
749    _sidebarResizeDrag: function(event)
750    {
751        var x = event.pageX + this._dragOffset;
752        var newWidth = Number.constrain(window.innerWidth - x, Preferences.minScriptsSidebarWidth, window.innerWidth * 0.66);
753
754        this.sidebarElement.style.width = newWidth + "px";
755        this.sidebarButtonsElement.style.width = newWidth + "px";
756        this.viewsContainerElement.style.right = newWidth + "px";
757        this.sidebarResizeWidgetElement.style.right = newWidth + "px";
758        this.sidebarResizeElement.style.right = (newWidth - 3) + "px";
759
760        event.preventDefault();
761    },
762
763    _updatePauseOnExceptionsButton: function()
764    {
765        if (InspectorController.pauseOnExceptions()) {
766            this.pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.");
767            this.pauseOnExceptionButton.addStyleClass("toggled-on");
768        } else {
769            this.pauseOnExceptionButton.title = WebInspector.UIString("Pause on exceptions.");
770            this.pauseOnExceptionButton.removeStyleClass("toggled-on");
771        }
772    },
773
774    _updateDebuggerButtons: function()
775    {
776        if (InspectorController.debuggerEnabled()) {
777            this.enableToggleButton.title = WebInspector.UIString("Debugging enabled. Click to disable.");
778            this.enableToggleButton.addStyleClass("toggled-on");
779            this.pauseOnExceptionButton.removeStyleClass("hidden");
780            this.panelEnablerView.visible = false;
781        } else {
782            this.enableToggleButton.title = WebInspector.UIString("Debugging disabled. Click to enable.");
783            this.enableToggleButton.removeStyleClass("toggled-on");
784            this.pauseOnExceptionButton.addStyleClass("hidden");
785            this.panelEnablerView.visible = true;
786        }
787
788        this._updatePauseOnExceptionsButton();
789
790        if (this._paused) {
791            this.pauseButton.addStyleClass("paused");
792
793            this.pauseButton.disabled = false;
794            this.stepOverButton.disabled = false;
795            this.stepIntoButton.disabled = false;
796            this.stepOutButton.disabled = false;
797
798            this.debuggerStatusElement.textContent = WebInspector.UIString("Paused");
799        } else {
800            this.pauseButton.removeStyleClass("paused");
801
802            this.pauseButton.disabled = this._waitingToPause;
803            this.stepOverButton.disabled = true;
804            this.stepIntoButton.disabled = true;
805            this.stepOutButton.disabled = true;
806
807            if (this._waitingToPause)
808                this.debuggerStatusElement.textContent = WebInspector.UIString("Pausing");
809            else if (this._stepping)
810                this.debuggerStatusElement.textContent = WebInspector.UIString("Stepping");
811            else
812                this.debuggerStatusElement.textContent = "";
813        }
814    },
815
816    _updateBackAndForwardButtons: function()
817    {
818        this.backButton.disabled = this._currentBackForwardIndex <= 0;
819        this.forwardButton.disabled = this._currentBackForwardIndex >= (this._backForwardList.length - 1);
820    },
821
822    _clearInterface: function()
823    {
824        this.sidebarPanes.callstack.update(null);
825        this.sidebarPanes.scopechain.update(null);
826
827        this._clearCurrentExecutionLine();
828        this._updateDebuggerButtons();
829    },
830
831    _goBack: function()
832    {
833        if (this._currentBackForwardIndex <= 0) {
834            console.error("Can't go back from index " + this._currentBackForwardIndex);
835            return;
836        }
837
838        this._showScriptOrResource(this._backForwardList[--this._currentBackForwardIndex], null, false, true);
839        this._updateBackAndForwardButtons();
840    },
841
842    _goForward: function()
843    {
844        if (this._currentBackForwardIndex >= this._backForwardList.length - 1) {
845            console.error("Can't go forward from index " + this._currentBackForwardIndex);
846            return;
847        }
848
849        this._showScriptOrResource(this._backForwardList[++this._currentBackForwardIndex], null, false, true);
850        this._updateBackAndForwardButtons();
851    },
852
853    _enableDebugging: function()
854    {
855        if (InspectorController.debuggerEnabled())
856            return;
857        this._toggleDebugging(this.panelEnablerView.alwaysEnabled);
858    },
859
860    _toggleDebugging: function(optionalAlways)
861    {
862        this._paused = false;
863        this._waitingToPause = false;
864        this._stepping = false;
865
866        if (InspectorController.debuggerEnabled())
867            InspectorController.disableDebugger(true);
868        else
869            InspectorController.enableDebugger(!!optionalAlways);
870    },
871
872    _togglePauseOnExceptions: function()
873    {
874        InspectorController.setPauseOnExceptions(!InspectorController.pauseOnExceptions());
875        this._updatePauseOnExceptionsButton();
876    },
877
878    _togglePause: function()
879    {
880        if (this._paused) {
881            this._paused = false;
882            this._waitingToPause = false;
883            InspectorController.resumeDebugger();
884        } else {
885            this._stepping = false;
886            this._waitingToPause = true;
887            InspectorController.pauseInDebugger();
888        }
889
890        this._clearInterface();
891    },
892
893    _stepOverClicked: function()
894    {
895        this._paused = false;
896        this._stepping = true;
897
898        this._clearInterface();
899
900        InspectorController.stepOverStatementInDebugger();
901    },
902
903    _stepIntoClicked: function()
904    {
905        this._paused = false;
906        this._stepping = true;
907
908        this._clearInterface();
909
910        InspectorController.stepIntoStatementInDebugger();
911    },
912
913    _stepOutClicked: function()
914    {
915        this._paused = false;
916        this._stepping = true;
917
918        this._clearInterface();
919
920        InspectorController.stepOutOfFunctionInDebugger();
921    }
922}
923
924WebInspector.ScriptsPanel.prototype.__proto__ = WebInspector.Panel.prototype;
925