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