Web Inspector: Extract common interface for StatusBarButton and StatusBarCombo
[WebKit-https.git] / Source / WebCore / inspector / front-end / ScriptsPanel.js
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  * Copyright (C) 2011 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 importScript("BreakpointsSidebarPane.js");
28 importScript("CallStackSidebarPane.js");
29 importScript("FilteredItemSelectionDialog.js");
30 importScript("JavaScriptSourceFrame.js");
31 importScript("NavigatorOverlayController.js");
32 importScript("NavigatorView.js");
33 importScript("RevisionHistoryView.js");
34 importScript("ScopeChainSidebarPane.js");
35 importScript("ScriptsNavigator.js");
36 importScript("ScriptsSearchScope.js");
37 importScript("SnippetJavaScriptSourceFrame.js");
38 importScript("StyleSheetOutlineDialog.js");
39 importScript("TabbedEditorContainer.js");
40 importScript("UISourceCodeFrame.js");
41 importScript("WatchExpressionsSidebarPane.js");
42 importScript("WorkersSidebarPane.js");
43
44 /**
45  * @constructor
46  * @implements {WebInspector.TabbedEditorContainerDelegate}
47  * @implements {WebInspector.ContextMenu.Provider}
48  * @extends {WebInspector.Panel}
49  * @param {WebInspector.Workspace=} workspaceForTest
50  */
51 WebInspector.ScriptsPanel = function(workspaceForTest)
52 {
53     WebInspector.Panel.call(this, "scripts");
54     this.registerRequiredCSS("scriptsPanel.css");
55
56     WebInspector.settings.navigatorWasOnceHidden = WebInspector.settings.createSetting("navigatorWasOnceHidden", false);
57     WebInspector.settings.debuggerSidebarHidden = WebInspector.settings.createSetting("debuggerSidebarHidden", false);
58
59     this._workspace = workspaceForTest || WebInspector.workspace;
60
61     function viewGetter()
62     {
63         return this.visibleView;
64     }
65     WebInspector.GoToLineDialog.install(this, viewGetter.bind(this));
66
67     var helpSection = WebInspector.shortcutsScreen.section(WebInspector.UIString("Sources Panel"));
68     this.debugToolbar = this._createDebugToolbar();
69
70     const initialDebugSidebarWidth = 225;
71     const minimumDebugSidebarWidthPercent = 50;
72     this.createSidebarView(this.element, WebInspector.SidebarView.SidebarPosition.Right, initialDebugSidebarWidth);
73     this.splitView.element.id = "scripts-split-view";
74     this.splitView.setMinimumSidebarWidth(Preferences.minScriptsSidebarWidth);
75     this.splitView.setMinimumMainWidthPercent(minimumDebugSidebarWidthPercent);
76
77     this.sidebarElement.appendChild(this.debugToolbar);
78
79     this.debugSidebarResizeWidgetElement = document.createElement("div");
80     this.debugSidebarResizeWidgetElement.id = "scripts-debug-sidebar-resizer-widget";
81     this.splitView.installResizer(this.debugSidebarResizeWidgetElement);
82
83     // Create scripts navigator
84     const initialNavigatorWidth = 225;
85     const minimumViewsContainerWidthPercent = 50;
86     this.editorView = new WebInspector.SidebarView(WebInspector.SidebarView.SidebarPosition.Left, "scriptsPanelNavigatorSidebarWidth", initialNavigatorWidth);
87     this.editorView.element.tabIndex = 0;
88
89     this.editorView.setMinimumSidebarWidth(Preferences.minScriptsSidebarWidth);
90     this.editorView.setMinimumMainWidthPercent(minimumViewsContainerWidthPercent);
91     this.editorView.show(this.splitView.mainElement);
92
93     this._navigator = new WebInspector.ScriptsNavigator();
94     this._navigator.view.show(this.editorView.sidebarElement);
95
96     this._editorContainer = new WebInspector.TabbedEditorContainer(this, "previouslyViewedFiles");
97     this._editorContainer.show(this.editorView.mainElement);
98
99     this._navigatorController = new WebInspector.NavigatorOverlayController(this.editorView, this._navigator.view, this._editorContainer.view);
100
101     this._navigator.addEventListener(WebInspector.ScriptsNavigator.Events.ScriptSelected, this._scriptSelected, this);
102     this._navigator.addEventListener(WebInspector.ScriptsNavigator.Events.SnippetCreationRequested, this._snippetCreationRequested, this);
103     this._navigator.addEventListener(WebInspector.ScriptsNavigator.Events.ItemRenamingRequested, this._itemRenamingRequested, this);
104     this._navigator.addEventListener(WebInspector.ScriptsNavigator.Events.FileRenamed, this._fileRenamed, this);
105
106     this._editorContainer.addEventListener(WebInspector.TabbedEditorContainer.Events.EditorSelected, this._editorSelected, this);
107     this._editorContainer.addEventListener(WebInspector.TabbedEditorContainer.Events.EditorClosed, this._editorClosed, this);
108
109     this.splitView.mainElement.appendChild(this.debugSidebarResizeWidgetElement);
110
111     this.sidebarPanes = {};
112     this.sidebarPanes.watchExpressions = new WebInspector.WatchExpressionsSidebarPane();
113     this.sidebarPanes.callstack = new WebInspector.CallStackSidebarPane();
114     this.sidebarPanes.scopechain = new WebInspector.ScopeChainSidebarPane();
115     this.sidebarPanes.jsBreakpoints = new WebInspector.JavaScriptBreakpointsSidebarPane(WebInspector.breakpointManager, this._showSourceLine.bind(this));
116     this.sidebarPanes.domBreakpoints = WebInspector.domBreakpointsSidebarPane;
117     this.sidebarPanes.xhrBreakpoints = new WebInspector.XHRBreakpointsSidebarPane();
118     this.sidebarPanes.eventListenerBreakpoints = new WebInspector.EventListenerBreakpointsSidebarPane();
119
120     if (InspectorFrontendHost.canInspectWorkers() && !WebInspector.WorkerManager.isWorkerFrontend()) {
121         WorkerAgent.enable();
122         this.sidebarPanes.workerList = new WebInspector.WorkersSidebarPane(WebInspector.workerManager);
123     }
124
125     this._debugSidebarContentsElement = document.createElement("div");
126     this._debugSidebarContentsElement.id = "scripts-debug-sidebar-contents";
127     this.sidebarElement.appendChild(this._debugSidebarContentsElement);
128
129     for (var pane in this.sidebarPanes)
130         this._debugSidebarContentsElement.appendChild(this.sidebarPanes[pane].element);
131
132     this.sidebarPanes.callstack.expanded = true;
133
134     this.sidebarPanes.scopechain.expanded = true;
135     this.sidebarPanes.jsBreakpoints.expanded = true;
136
137     this.sidebarPanes.callstack.registerShortcuts(this.registerShortcuts.bind(this));
138     this.registerShortcuts(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.EvaluateSelectionInConsole, this._evaluateSelectionInConsole.bind(this));
139     this.registerShortcuts(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.GoToMember, this._showOutlineDialog.bind(this));
140     this.registerShortcuts(WebInspector.ScriptsPanelDescriptor.ShortcutKeys.ToggleBreakpoint, this._toggleBreakpoint.bind(this));
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.enableToggleButton = new WebInspector.StatusBarButton("", "enable-toggle-status-bar-item");
150     this.enableToggleButton.addEventListener("click", this._toggleDebugging, this);
151     if (!Capabilities.debuggerCausesRecompilation)
152         this.enableToggleButton.element.addStyleClass("hidden");
153
154     this._pauseOnExceptionButton = new WebInspector.StatusBarButton("", "scripts-pause-on-exceptions-status-bar-item", 3);
155     this._pauseOnExceptionButton.addEventListener("click", this._togglePauseOnExceptions, this);
156
157     this._toggleFormatSourceButton = new WebInspector.StatusBarButton(WebInspector.UIString("Pretty print"), "scripts-toggle-pretty-print-status-bar-item");
158     this._toggleFormatSourceButton.toggled = false;
159     this._toggleFormatSourceButton.addEventListener("click", this._toggleFormatSource, this);
160
161     this._scriptViewStatusBarItemsContainer = document.createElement("div");
162     this._scriptViewStatusBarItemsContainer.style.display = "inline-block";
163
164     this._installDebuggerSidebarController();
165
166     this._sourceFramesByUISourceCode = new Map();
167     this._updateDebuggerButtons();
168     this._pauseOnExceptionStateChanged();
169     if (WebInspector.debuggerModel.isPaused())
170         this._debuggerPaused();
171
172     WebInspector.settings.pauseOnExceptionStateString.addChangeListener(this._pauseOnExceptionStateChanged, this);
173     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._debuggerWasEnabled, this);
174     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasDisabled, this._debuggerWasDisabled, this);
175     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
176     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
177     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.CallFrameSelected, this._callFrameSelected, this);
178     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ConsoleCommandEvaluatedInSelectedCallFrame, this._consoleCommandEvaluatedInSelectedCallFrame, this);
179     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ExecutionLineChanged, this._executionLineChanged, this);
180     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointsActiveStateChanged, this._breakpointsActiveStateChanged, this);
181
182     WebInspector.startBatchUpdate();
183     var uiSourceCodes = this._workspace.uiSourceCodes();
184     for (var i = 0; i < uiSourceCodes.length; ++i)
185         this._addUISourceCode(uiSourceCodes[i]);
186     WebInspector.endBatchUpdate();
187
188     this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
189     this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeRemoved, this._uiSourceCodeRemoved, this);
190     this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.TemporaryUISourceCodeRemoved, this._uiSourceCodeRemoved, this);
191     this._workspace.addEventListener(WebInspector.Workspace.Events.ProjectWillReset, this._reset.bind(this), this);
192
193     WebInspector.advancedSearchController.registerSearchScope(new WebInspector.ScriptsSearchScope(this._workspace));
194 }
195
196 WebInspector.ScriptsPanel.prototype = {
197     get statusBarItems()
198     {
199         return [this.enableToggleButton.element, this._pauseOnExceptionButton.element, this._toggleFormatSourceButton.element, this._scriptViewStatusBarItemsContainer];
200     },
201
202     defaultFocusedElement: function()
203     {
204         return this._navigator.view.defaultFocusedElement();
205     },
206
207     get paused()
208     {
209         return this._paused;
210     },
211
212     wasShown: function()
213     {
214         WebInspector.Panel.prototype.wasShown.call(this);
215         this._debugSidebarContentsElement.insertBefore(this.sidebarPanes.domBreakpoints.element, this.sidebarPanes.xhrBreakpoints.element);
216         this.sidebarPanes.watchExpressions.show();
217
218         this._navigatorController.wasShown();
219     },
220
221     willHide: function()
222     {
223         WebInspector.Panel.prototype.willHide.call(this);
224         WebInspector.closeViewInDrawer();
225     },
226
227     /**
228      * @param {WebInspector.Event} event
229      */
230     _uiSourceCodeAdded: function(event)
231     {
232         var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
233         this._addUISourceCode(uiSourceCode);
234     },
235
236     /**
237      * @param {WebInspector.UISourceCode} uiSourceCode
238      */
239     _addUISourceCode: function(uiSourceCode)
240     {
241         if (this._toggleFormatSourceButton.toggled)
242             uiSourceCode.setFormatted(true);
243
244         this._navigator.addUISourceCode(uiSourceCode);
245         this._editorContainer.addUISourceCode(uiSourceCode);
246     },
247
248     _uiSourceCodeRemoved: function(event)
249     {
250         var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
251         this._editorContainer.removeUISourceCode(uiSourceCode);
252         this._navigator.removeUISourceCode(uiSourceCode);
253         this._removeSourceFrame(uiSourceCode);
254     },
255
256     _consoleCommandEvaluatedInSelectedCallFrame: function(event)
257     {
258         this.sidebarPanes.scopechain.update(WebInspector.debuggerModel.selectedCallFrame());
259     },
260
261     _debuggerPaused: function()
262     {
263         var details = WebInspector.debuggerModel.debuggerPausedDetails();
264
265         this._paused = true;
266         this._waitingToPause = false;
267         this._stepping = false;
268
269         this._updateDebuggerButtons();
270
271         WebInspector.inspectorView.setCurrentPanel(this);
272         this.sidebarPanes.callstack.update(details.callFrames);
273
274         if (details.reason === WebInspector.DebuggerModel.BreakReason.DOM) {
275             this.sidebarPanes.domBreakpoints.highlightBreakpoint(details.auxData);
276             function didCreateBreakpointHitStatusMessage(element)
277             {
278                 this.sidebarPanes.callstack.setStatus(element);
279             }
280             this.sidebarPanes.domBreakpoints.createBreakpointHitStatusMessage(details.auxData, didCreateBreakpointHitStatusMessage.bind(this));
281         } else if (details.reason === WebInspector.DebuggerModel.BreakReason.EventListener) {
282             var eventName = details.auxData.eventName;
283             this.sidebarPanes.eventListenerBreakpoints.highlightBreakpoint(details.auxData.eventName);
284             var eventNameForUI = WebInspector.EventListenerBreakpointsSidebarPane.eventNameForUI(eventName);
285             this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a \"%s\" Event Listener.", eventNameForUI));
286         } else if (details.reason === WebInspector.DebuggerModel.BreakReason.XHR) {
287             this.sidebarPanes.xhrBreakpoints.highlightBreakpoint(details.auxData["breakpointURL"]);
288             this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a XMLHttpRequest."));
289         } else if (details.reason === WebInspector.DebuggerModel.BreakReason.Exception)
290             this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on exception: '%s'.", details.auxData.description));
291         else if (details.reason === WebInspector.DebuggerModel.BreakReason.Assert)
292             this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on assertion."));
293         else if (details.reason === WebInspector.DebuggerModel.BreakReason.CSPViolation)
294             this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a script blocked due to Content Security Policy directive: \"%s\".", details.auxData["directiveText"]));
295         else {
296             function didGetUILocation(uiLocation)
297             {
298                 var breakpoint = WebInspector.breakpointManager.findBreakpoint(uiLocation.uiSourceCode, uiLocation.lineNumber);
299                 if (!breakpoint)
300                     return;
301                 this.sidebarPanes.jsBreakpoints.highlightBreakpoint(breakpoint);
302                 this.sidebarPanes.callstack.setStatus(WebInspector.UIString("Paused on a JavaScript breakpoint."));
303             }
304             details.callFrames[0].createLiveLocation(didGetUILocation.bind(this));
305         }
306
307         this._showDebuggerSidebar();
308         this._toggleDebuggerSidebarButton.setEnabled(false);
309         window.focus();
310         InspectorFrontendHost.bringToFront();
311     },
312
313     _debuggerResumed: function()
314     {
315         this._paused = false;
316         this._waitingToPause = false;
317         this._stepping = false;
318
319         this._clearInterface();
320         this._toggleDebuggerSidebarButton.setEnabled(true);
321     },
322
323     _debuggerWasEnabled: function()
324     {
325         this._updateDebuggerButtons();
326     },
327
328     _debuggerWasDisabled: function()
329     {
330         this._reset();
331     },
332
333     _reset: function()
334     {
335         delete this.currentQuery;
336         this.searchCanceled();
337
338         this._debuggerResumed();
339
340         delete this._currentUISourceCode;
341         this._navigator.reset();
342         this._editorContainer.reset();
343         this._updateScriptViewStatusBarItems();
344         this.sidebarPanes.jsBreakpoints.reset();
345         this.sidebarPanes.watchExpressions.reset();
346
347         var uiSourceCodes = this._workspace.uiSourceCodes();
348         for (var i = 0; i < uiSourceCodes.length; ++i)
349             this._removeSourceFrame(uiSourceCodes[i]);
350     },
351
352     get visibleView()
353     {
354         return this._editorContainer.visibleView;
355     },
356
357     _updateScriptViewStatusBarItems: function()
358     {
359         this._scriptViewStatusBarItemsContainer.removeChildren();
360
361         var sourceFrame = this.visibleView;
362         if (sourceFrame) {
363             var statusBarItems = sourceFrame.statusBarItems() || [];
364             for (var i = 0; i < statusBarItems.length; ++i)
365                 this._scriptViewStatusBarItemsContainer.appendChild(statusBarItems[i]);
366         }
367     },
368
369     canShowAnchorLocation: function(anchor)
370     {
371         if (WebInspector.debuggerModel.debuggerEnabled() && anchor.uiSourceCode)
372             return true;
373         var uiSourceCodes = this._workspace.uiSourceCodes();
374         for (var i = 0; i < uiSourceCodes.length; ++i) {
375             if (uiSourceCodes[i].url === anchor.href) {
376                 anchor.uiSourceCode = uiSourceCodes[i];
377                 return true;
378             }
379         }
380         return false;
381     },
382
383     showAnchorLocation: function(anchor)
384     {
385         this._showSourceLine(anchor.uiSourceCode, anchor.lineNumber);
386     },
387
388     /**
389      * @param {WebInspector.UISourceCode} uiSourceCode
390      * @param {number} lineNumber
391      */
392     showUISourceCode: function(uiSourceCode, lineNumber)
393     {
394         this._showSourceLine(uiSourceCode, lineNumber);
395     },
396
397     /**
398      * @param {WebInspector.UISourceCode} uiSourceCode
399      * @param {number=} lineNumber
400      */
401     _showSourceLine: function(uiSourceCode, lineNumber)
402     {
403         var sourceFrame = this._showFile(uiSourceCode);
404         if (typeof lineNumber === "number")
405             sourceFrame.highlightLine(lineNumber);
406         sourceFrame.focus();
407     },
408
409     /**
410      * @param {WebInspector.UISourceCode} uiSourceCode
411      * @return {WebInspector.SourceFrame}
412      */
413     _showFile: function(uiSourceCode)
414     {
415         var sourceFrame = this._getOrCreateSourceFrame(uiSourceCode);
416         if (this._currentUISourceCode === uiSourceCode)
417             return sourceFrame;
418         this._currentUISourceCode = uiSourceCode;
419
420         if (this._navigator.isScriptSourceAdded(uiSourceCode))
421             this._navigator.revealUISourceCode(uiSourceCode);
422         this._editorContainer.showFile(uiSourceCode);
423         this._updateScriptViewStatusBarItems();
424
425         return sourceFrame;
426     },
427
428     /**
429      * @param {WebInspector.UISourceCode} uiSourceCode
430      * @return {WebInspector.SourceFrame}
431      */
432     _createSourceFrame: function(uiSourceCode)
433     {
434         var sourceFrame;
435         switch (uiSourceCode.contentType()) {
436         case WebInspector.resourceTypes.Script:
437             if (uiSourceCode.isSnippet && !uiSourceCode.isTemporary)
438                 sourceFrame = new WebInspector.SnippetJavaScriptSourceFrame(this, uiSourceCode);
439             else
440                 sourceFrame = new WebInspector.JavaScriptSourceFrame(this, uiSourceCode);
441             break;
442         case WebInspector.resourceTypes.Document:
443             sourceFrame = new WebInspector.JavaScriptSourceFrame(this, uiSourceCode);
444             break;
445         case WebInspector.resourceTypes.Stylesheet:
446         default:
447             sourceFrame = new WebInspector.UISourceCodeFrame(uiSourceCode);
448         break;
449         }
450         this._sourceFramesByUISourceCode.put(uiSourceCode, sourceFrame);
451         return sourceFrame;
452     },
453
454     /**
455      * @param {WebInspector.UISourceCode} uiSourceCode
456      * @return {WebInspector.SourceFrame}
457      */
458     _getOrCreateSourceFrame: function(uiSourceCode)
459     {
460         return this._sourceFramesByUISourceCode.get(uiSourceCode) || this._createSourceFrame(uiSourceCode);
461     },
462
463     /**
464      * @param {WebInspector.UISourceCode} uiSourceCode
465      * @return {WebInspector.SourceFrame}
466      */
467     viewForFile: function(uiSourceCode)
468     {
469         return this._getOrCreateSourceFrame(uiSourceCode);
470     },
471
472     /**
473      * @param {WebInspector.UISourceCode} uiSourceCode
474      */
475     _removeSourceFrame: function(uiSourceCode)
476     {
477         var sourceFrame = this._sourceFramesByUISourceCode.get(uiSourceCode);
478         if (!sourceFrame)
479             return;
480         this._sourceFramesByUISourceCode.remove(uiSourceCode);
481         sourceFrame.detach();
482     },
483
484     _clearCurrentExecutionLine: function()
485     {
486         if (this._executionSourceFrame)
487             this._executionSourceFrame.clearExecutionLine();
488         delete this._executionSourceFrame;
489     },
490
491     _executionLineChanged: function(event)
492     {
493         var uiLocation = event.data;
494
495         this._clearCurrentExecutionLine();
496         if (!uiLocation)
497             return;
498         var sourceFrame = this._getOrCreateSourceFrame(uiLocation.uiSourceCode);
499         sourceFrame.setExecutionLine(uiLocation.lineNumber);
500         this._executionSourceFrame = sourceFrame;
501     },
502
503     _revealExecutionLine: function(uiLocation)
504     {
505         var uiSourceCode = uiLocation.uiSourceCode;
506         // Some scripts (anonymous and snippets evaluations) are not added to files select by default.
507         if (uiSourceCode.isTemporary) {
508             if (this._currentUISourceCode && this._currentUISourceCode.scriptFile() && this._currentUISourceCode.scriptFile().isDivergingFromVM())
509                 return;
510             this._editorContainer.addUISourceCode(uiSourceCode);
511             if (uiSourceCode.formatted() !== this._toggleFormatSourceButton.toggled)
512                 uiSourceCode.setFormatted(this._toggleFormatSourceButton.toggled);
513         }
514         var sourceFrame = this._showFile(uiSourceCode);
515         sourceFrame.revealLine(uiLocation.lineNumber);
516         sourceFrame.focus();
517     },
518
519     _callFrameSelected: function(event)
520     {
521         var callFrame = event.data;
522
523         if (!callFrame)
524             return;
525
526         this.sidebarPanes.scopechain.update(callFrame);
527         this.sidebarPanes.watchExpressions.refreshExpressions();
528         this.sidebarPanes.callstack.setSelectedCallFrame(callFrame);
529         callFrame.createLiveLocation(this._revealExecutionLine.bind(this));
530     },
531
532     _editorClosed: function(event)
533     {
534         this._navigatorController.hideNavigatorOverlay();
535         var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
536
537         if (this._currentUISourceCode === uiSourceCode)
538             delete this._currentUISourceCode;
539
540         // ScriptsNavigator does not need to update on EditorClosed.
541         this._updateScriptViewStatusBarItems();
542         WebInspector.searchController.resetSearch();
543     },
544
545     _editorSelected: function(event)
546     {
547         var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
548         var sourceFrame = this._showFile(uiSourceCode);
549         this._navigatorController.hideNavigatorOverlay();
550         sourceFrame.focus();
551         WebInspector.searchController.resetSearch();
552     },
553
554     _scriptSelected: function(event)
555     {
556         var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data.uiSourceCode);
557         var sourceFrame = this._showFile(uiSourceCode);
558         this._navigatorController.hideNavigatorOverlay();
559         if (sourceFrame && event.data.focusSource)
560             sourceFrame.focus();
561     },
562
563     _pauseOnExceptionStateChanged: function()
564     {
565         var pauseOnExceptionsState = WebInspector.settings.pauseOnExceptionStateString.get();
566         switch (pauseOnExceptionsState) {
567         case WebInspector.DebuggerModel.PauseOnExceptionsState.DontPauseOnExceptions:
568             this._pauseOnExceptionButton.title = WebInspector.UIString("Don't pause on exceptions.\nClick to Pause on all exceptions.");
569             break;
570         case WebInspector.DebuggerModel.PauseOnExceptionsState.PauseOnAllExceptions:
571             this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on all exceptions.\nClick to Pause on uncaught exceptions.");
572             break;
573         case WebInspector.DebuggerModel.PauseOnExceptionsState.PauseOnUncaughtExceptions:
574             this._pauseOnExceptionButton.title = WebInspector.UIString("Pause on uncaught exceptions.\nClick to Not pause on exceptions.");
575             break;
576         }
577         this._pauseOnExceptionButton.state = pauseOnExceptionsState;
578     },
579
580     _updateDebuggerButtons: function()
581     {
582         if (WebInspector.debuggerModel.debuggerEnabled()) {
583             this.enableToggleButton.title = WebInspector.UIString("Debugging enabled. Click to disable.");
584             this.enableToggleButton.toggled = true;
585             this._pauseOnExceptionButton.visible = true;
586             this.panelEnablerView.detach();
587         } else {
588             this.enableToggleButton.title = WebInspector.UIString("Debugging disabled. Click to enable.");
589             this.enableToggleButton.toggled = false;
590             this._pauseOnExceptionButton.visible = false;
591             this.panelEnablerView.show(this.element);
592         }
593
594         if (this._paused) {
595             this._updateButtonTitle(this.pauseButton, WebInspector.UIString("Resume script execution (%s)."))
596             this.pauseButton.addStyleClass("paused");
597
598             this.pauseButton.disabled = false;
599             this.stepOverButton.disabled = false;
600             this.stepIntoButton.disabled = false;
601             this.stepOutButton.disabled = false;
602
603             this.debuggerStatusElement.textContent = WebInspector.UIString("Paused");
604         } else {
605             this._updateButtonTitle(this.pauseButton, WebInspector.UIString("Pause script execution (%s)."))
606             this.pauseButton.removeStyleClass("paused");
607
608             this.pauseButton.disabled = this._waitingToPause;
609             this.stepOverButton.disabled = true;
610             this.stepIntoButton.disabled = true;
611             this.stepOutButton.disabled = true;
612
613             if (this._waitingToPause)
614                 this.debuggerStatusElement.textContent = WebInspector.UIString("Pausing");
615             else if (this._stepping)
616                 this.debuggerStatusElement.textContent = WebInspector.UIString("Stepping");
617             else
618                 this.debuggerStatusElement.textContent = "";
619         }
620     },
621
622     _clearInterface: function()
623     {
624         this.sidebarPanes.callstack.update(null);
625         this.sidebarPanes.scopechain.update(null);
626         this.sidebarPanes.jsBreakpoints.clearBreakpointHighlight();
627         this.sidebarPanes.domBreakpoints.clearBreakpointHighlight();
628         this.sidebarPanes.eventListenerBreakpoints.clearBreakpointHighlight();
629         this.sidebarPanes.xhrBreakpoints.clearBreakpointHighlight();
630
631         this._clearCurrentExecutionLine();
632         this._updateDebuggerButtons();
633     },
634
635     _enableDebugging: function()
636     {
637         this._toggleDebugging(this.panelEnablerView.alwaysEnabled);
638     },
639
640     _toggleDebugging: function(optionalAlways)
641     {
642         this._paused = false;
643         this._waitingToPause = false;
644         this._stepping = false;
645
646         if (WebInspector.debuggerModel.debuggerEnabled()) {
647             WebInspector.settings.debuggerEnabled.set(false);
648             WebInspector.debuggerModel.disableDebugger();
649         } else {
650             WebInspector.settings.debuggerEnabled.set(!!optionalAlways);
651             WebInspector.debuggerModel.enableDebugger();
652         }
653     },
654
655     _togglePauseOnExceptions: function()
656     {
657         var nextStateMap = {};
658         var stateEnum = WebInspector.DebuggerModel.PauseOnExceptionsState;
659         nextStateMap[stateEnum.DontPauseOnExceptions] = stateEnum.PauseOnAllExceptions;
660         nextStateMap[stateEnum.PauseOnAllExceptions] = stateEnum.PauseOnUncaughtExceptions;
661         nextStateMap[stateEnum.PauseOnUncaughtExceptions] = stateEnum.DontPauseOnExceptions;
662         WebInspector.settings.pauseOnExceptionStateString.set(nextStateMap[this._pauseOnExceptionButton.state]);
663     },
664
665     _togglePause: function()
666     {
667         if (this._paused) {
668             this._paused = false;
669             this._waitingToPause = false;
670             DebuggerAgent.resume();
671         } else {
672             this._stepping = false;
673             this._waitingToPause = true;
674             DebuggerAgent.pause();
675         }
676
677         this._clearInterface();
678     },
679
680     _stepOverClicked: function()
681     {
682         if (!this._paused)
683             return;
684
685         this._paused = false;
686         this._stepping = true;
687
688         this._clearInterface();
689
690         DebuggerAgent.stepOver();
691     },
692
693     _stepIntoClicked: function()
694     {
695         if (!this._paused)
696             return;
697
698         this._paused = false;
699         this._stepping = true;
700
701         this._clearInterface();
702
703         DebuggerAgent.stepInto();
704     },
705
706     _stepOutClicked: function()
707     {
708         if (!this._paused)
709             return;
710
711         this._paused = false;
712         this._stepping = true;
713
714         this._clearInterface();
715
716         DebuggerAgent.stepOut();
717     },
718
719     _toggleBreakpointsClicked: function(event)
720     {
721         WebInspector.debuggerModel.setBreakpointsActive(!WebInspector.debuggerModel.breakpointsActive());
722     },
723
724     _breakpointsActiveStateChanged: function(event)
725     {
726         var active = event.data;
727         this._toggleBreakpointsButton.toggled = active;
728         if (active) {
729             this._toggleBreakpointsButton.title = WebInspector.UIString("Deactivate breakpoints.");
730             WebInspector.inspectorView.element.removeStyleClass("breakpoints-deactivated");
731             this.sidebarPanes.jsBreakpoints.listElement.removeStyleClass("breakpoints-list-deactivated");
732         } else {
733             this._toggleBreakpointsButton.title = WebInspector.UIString("Activate breakpoints.");
734             WebInspector.inspectorView.element.addStyleClass("breakpoints-deactivated");
735             this.sidebarPanes.jsBreakpoints.listElement.addStyleClass("breakpoints-list-deactivated");
736         }
737     },
738
739     _evaluateSelectionInConsole: function()
740     {
741         var selection = window.getSelection();
742         if (selection.type === "Range" && !selection.isCollapsed)
743             WebInspector.evaluateInConsole(selection.toString());
744     },
745
746     _createDebugToolbar: function()
747     {
748         var debugToolbar = document.createElement("div");
749         debugToolbar.className = "status-bar";
750         debugToolbar.id = "scripts-debug-toolbar";
751
752         var title, handler;
753         var platformSpecificModifier = WebInspector.KeyboardShortcut.Modifiers.CtrlOrMeta;
754
755         // Continue.
756         handler = this._togglePause.bind(this);
757         this.pauseButton = this._createButtonAndRegisterShortcuts("scripts-pause", "", handler, WebInspector.ScriptsPanelDescriptor.ShortcutKeys.PauseContinue);
758         debugToolbar.appendChild(this.pauseButton);
759
760         // Step over.
761         title = WebInspector.UIString("Step over next function call (%s).");
762         handler = this._stepOverClicked.bind(this);
763         this.stepOverButton = this._createButtonAndRegisterShortcuts("scripts-step-over", title, handler, WebInspector.ScriptsPanelDescriptor.ShortcutKeys.StepOver);
764         debugToolbar.appendChild(this.stepOverButton);
765
766         // Step into.
767         title = WebInspector.UIString("Step into next function call (%s).");
768         handler = this._stepIntoClicked.bind(this);
769         this.stepIntoButton = this._createButtonAndRegisterShortcuts("scripts-step-into", title, handler, WebInspector.ScriptsPanelDescriptor.ShortcutKeys.StepInto);
770         debugToolbar.appendChild(this.stepIntoButton);
771
772         // Step out.
773         title = WebInspector.UIString("Step out of current function (%s).");
774         handler = this._stepOutClicked.bind(this);
775         this.stepOutButton = this._createButtonAndRegisterShortcuts("scripts-step-out", title, handler, WebInspector.ScriptsPanelDescriptor.ShortcutKeys.StepOut);
776         debugToolbar.appendChild(this.stepOutButton);
777
778         this._toggleBreakpointsButton = new WebInspector.StatusBarButton(WebInspector.UIString("Deactivate breakpoints."), "toggle-breakpoints");
779         this._toggleBreakpointsButton.toggled = true;
780         this._toggleBreakpointsButton.addEventListener("click", this._toggleBreakpointsClicked, this);
781         debugToolbar.appendChild(this._toggleBreakpointsButton.element);
782
783         this.debuggerStatusElement = document.createElement("div");
784         this.debuggerStatusElement.id = "scripts-debugger-status";
785         debugToolbar.appendChild(this.debuggerStatusElement);
786
787         return debugToolbar;
788     },
789
790     _updateButtonTitle: function(button, buttonTitle)
791     {
792         button.buttonTitle = buttonTitle;
793         var hasShortcuts = button.shortcuts && button.shortcuts.length;
794         if (hasShortcuts)
795             button.title = String.vsprintf(buttonTitle, [button.shortcuts[0].name]);
796         else
797             button.title = buttonTitle;
798     },
799
800     /**
801      * @param {string} buttonId
802      * @param {string} buttonTitle
803      * @param {function(Event)} handler
804      * @param {!Array.<!WebInspector.KeyboardShortcut.Descriptor>} shortcuts
805      */
806     _createButtonAndRegisterShortcuts: function(buttonId, buttonTitle, handler, shortcuts)
807     {
808         var button = document.createElement("button");
809         button.className = "status-bar-item";
810         button.id = buttonId;
811         button.shortcuts = shortcuts;
812         this._updateButtonTitle(button, buttonTitle);
813         button.disabled = true;
814         button.appendChild(document.createElement("img"));
815         button.addEventListener("click", handler, false);
816
817         this.registerShortcuts(shortcuts, handler);
818
819         return button;
820     },
821
822     searchCanceled: function()
823     {
824         if (this._searchView)
825             this._searchView.searchCanceled();
826
827         delete this._searchView;
828         delete this._searchQuery;
829     },
830
831     /**
832      * @param {string} query
833      */
834     performSearch: function(query)
835     {
836         WebInspector.searchController.updateSearchMatchesCount(0, this);
837
838         if (!this.visibleView)
839             return;
840
841         // Call searchCanceled since it will reset everything we need before doing a new search.
842         this.searchCanceled();
843
844         this._searchView = this.visibleView;
845         this._searchQuery = query;
846
847         function finishedCallback(view, searchMatches)
848         {
849             if (!searchMatches)
850                 return;
851
852             WebInspector.searchController.updateSearchMatchesCount(searchMatches, this);
853             view.jumpToNextSearchResult();
854             WebInspector.searchController.updateCurrentMatchIndex(view.currentSearchResultIndex, this);
855         }
856
857         this._searchView.performSearch(query, finishedCallback.bind(this));
858     },
859
860     jumpToNextSearchResult: function()
861     {
862         if (!this._searchView)
863             return;
864
865         if (this._searchView !== this.visibleView) {
866             this.performSearch(this._searchQuery);
867             return;
868         }
869
870         if (this._searchView.showingLastSearchResult())
871             this._searchView.jumpToFirstSearchResult();
872         else
873             this._searchView.jumpToNextSearchResult();
874         WebInspector.searchController.updateCurrentMatchIndex(this._searchView.currentSearchResultIndex, this);
875         return true;
876     },
877
878     jumpToPreviousSearchResult: function()
879     {
880         if (!this._searchView)
881             return false;
882
883         if (this._searchView !== this.visibleView) {
884             this.performSearch(this._searchQuery);
885             if (this._searchView)
886                 this._searchView.jumpToLastSearchResult();
887             return;
888         }
889
890         if (this._searchView.showingFirstSearchResult())
891             this._searchView.jumpToLastSearchResult();
892         else
893             this._searchView.jumpToPreviousSearchResult();
894         WebInspector.searchController.updateCurrentMatchIndex(this._searchView.currentSearchResultIndex, this);
895     },
896
897     /**
898      * @return {boolean}
899      */
900     canSearchAndReplace: function()
901     {
902         var view = /** @type {WebInspector.SourceFrame} */ (this.visibleView);
903         return !!view && view.canEditSource();
904     },
905
906     /**
907      * @param {string} text
908      */
909     replaceSelectionWith: function(text)
910     {
911         var view = /** @type {WebInspector.SourceFrame} */ (this.visibleView);
912         view.replaceSearchMatchWith(text);
913     },
914
915     /**
916      * @param {string} query
917      * @param {string} text
918      */
919     replaceAllWith: function(query, text)
920     {
921         var view = /** @type {WebInspector.SourceFrame} */ (this.visibleView);
922         view.replaceAllWith(query, text);
923     },
924
925     _toggleFormatSource: function()
926     {
927         this._toggleFormatSourceButton.toggled = !this._toggleFormatSourceButton.toggled;
928         var uiSourceCodes = this._workspace.uiSourceCodes();
929         for (var i = 0; i < uiSourceCodes.length; ++i)
930             uiSourceCodes[i].setFormatted(this._toggleFormatSourceButton.toggled);
931     },
932
933     addToWatch: function(expression)
934     {
935         this.sidebarPanes.watchExpressions.addExpression(expression);
936     },
937
938     _toggleBreakpoint: function()
939     {
940         var sourceFrame = this.visibleView;
941         if (!sourceFrame)
942             return;
943
944         if (sourceFrame instanceof WebInspector.JavaScriptSourceFrame) {
945             var javaScriptSourceFrame = /** @type {WebInspector.JavaScriptSourceFrame} */ (sourceFrame);
946             javaScriptSourceFrame.toggleBreakpointOnCurrentLine();
947         }            
948     },
949
950     _showOutlineDialog: function()
951     {
952         var uiSourceCode = this._editorContainer.currentFile();
953         if (!uiSourceCode)
954             return;
955
956         switch (uiSourceCode.contentType()) {
957         case WebInspector.resourceTypes.Document:
958         case WebInspector.resourceTypes.Script:
959             WebInspector.JavaScriptOutlineDialog.show(this.visibleView, uiSourceCode);
960             break;
961         case WebInspector.resourceTypes.Stylesheet:
962             WebInspector.StyleSheetOutlineDialog.show(this.visibleView, uiSourceCode);
963             break;
964         }
965     },
966
967     _installDebuggerSidebarController: function()
968     {
969         this._toggleDebuggerSidebarButton = new WebInspector.StatusBarButton(WebInspector.UIString("Hide debugger"), "scripts-debugger-show-hide-button", 3);
970         this._toggleDebuggerSidebarButton.state = "shown";
971         this._toggleDebuggerSidebarButton.addEventListener("click", clickHandler, this);
972
973         function clickHandler()
974         {
975             if (this._toggleDebuggerSidebarButton.state === "shown")
976                 this._hideDebuggerSidebar();
977             else
978                 this._showDebuggerSidebar();
979         }
980         this.editorView.element.appendChild(this._toggleDebuggerSidebarButton.element);
981
982         if (WebInspector.settings.debuggerSidebarHidden.get())
983             this._hideDebuggerSidebar();
984
985     },
986
987     _showDebuggerSidebar: function()
988     {
989         if (this._toggleDebuggerSidebarButton.state === "shown")
990             return;
991         this._toggleDebuggerSidebarButton.state = "shown";
992         this._toggleDebuggerSidebarButton.title = WebInspector.UIString("Hide debugger");
993         this.splitView.showSidebarElement();
994         this.debugSidebarResizeWidgetElement.removeStyleClass("hidden");
995         WebInspector.settings.debuggerSidebarHidden.set(false);
996     },
997
998     _hideDebuggerSidebar: function()
999     {
1000         if (this._toggleDebuggerSidebarButton.state === "hidden")
1001             return;
1002         this._toggleDebuggerSidebarButton.state = "hidden";
1003         this._toggleDebuggerSidebarButton.title = WebInspector.UIString("Show debugger");
1004         this.splitView.hideSidebarElement();
1005         this.debugSidebarResizeWidgetElement.addStyleClass("hidden");
1006         WebInspector.settings.debuggerSidebarHidden.set(true);
1007     },
1008
1009     _fileRenamed: function(event)
1010     {
1011         var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data.uiSourceCode);
1012         var name = /** @type {string} */ (event.data.name);
1013         if (!uiSourceCode.isSnippet)
1014             return;
1015         WebInspector.scriptSnippetModel.renameScriptSnippet(uiSourceCode, name);
1016     },
1017         
1018     /**
1019      * @param {WebInspector.Event} event
1020      */
1021     _snippetCreationRequested: function(event)
1022     {
1023         var uiSourceCode = WebInspector.scriptSnippetModel.createScriptSnippet();
1024         this._showSourceLine(uiSourceCode);
1025         
1026         var shouldHideNavigator = !this._navigatorController.isNavigatorPinned();
1027         if (this._navigatorController.isNavigatorHidden())
1028             this._navigatorController.showNavigatorOverlay();
1029         this._navigator.rename(uiSourceCode, callback.bind(this));
1030     
1031         /**
1032          * @param {boolean} committed
1033          */
1034         function callback(committed)
1035         {
1036             if (shouldHideNavigator)
1037                 this._navigatorController.hideNavigatorOverlay();
1038
1039             if (!committed) {
1040                 WebInspector.scriptSnippetModel.deleteScriptSnippet(uiSourceCode);
1041                 return;
1042             }
1043
1044             this._showSourceLine(uiSourceCode);
1045         }
1046     },
1047
1048     /**
1049      * @param {WebInspector.Event} event
1050      */
1051     _itemRenamingRequested: function(event)
1052     {
1053         var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (event.data);
1054         
1055         var shouldHideNavigator = !this._navigatorController.isNavigatorPinned();
1056         if (this._navigatorController.isNavigatorHidden())
1057             this._navigatorController.showNavigatorOverlay();
1058         this._navigator.rename(uiSourceCode, callback.bind(this));
1059     
1060         /**
1061          * @param {boolean} committed
1062          */
1063         function callback(committed)
1064         {
1065             if (shouldHideNavigator && committed) {
1066                 this._navigatorController.hideNavigatorOverlay();
1067                 this._showSourceLine(uiSourceCode);
1068             }
1069         }
1070     },
1071
1072     /**
1073      * @param {WebInspector.UISourceCode} uiSourceCode
1074      */
1075     _showLocalHistory: function(uiSourceCode)
1076     {
1077         WebInspector.RevisionHistoryView.showHistory(uiSourceCode);
1078     },
1079
1080     /** 
1081      * @param {WebInspector.ContextMenu} contextMenu
1082      * @param {Object} target
1083      */
1084     appendApplicableItems: function(event, contextMenu, target)
1085     {
1086         this._appendUISourceCodeItems(contextMenu, target);
1087         this._appendFunctionItems(contextMenu, target);
1088     },
1089
1090     /** 
1091      * @param {WebInspector.ContextMenu} contextMenu
1092      * @param {Object} target
1093      */
1094     _appendUISourceCodeItems: function(contextMenu, target)
1095     {
1096         if (!(target instanceof WebInspector.UISourceCode))
1097             return;
1098
1099         var uiSourceCode = /** @type {WebInspector.UISourceCode} */ (target);
1100         contextMenu.appendItem(WebInspector.UIString("Local modifications..."), this._showLocalHistory.bind(this, uiSourceCode));
1101         var resource = WebInspector.resourceForURL(uiSourceCode.url);
1102         if (resource && resource.request)
1103             contextMenu.appendApplicableItems(resource.request);
1104     },
1105
1106     /** 
1107      * @param {WebInspector.ContextMenu} contextMenu
1108      * @param {Object} target
1109      */
1110     _appendFunctionItems: function(contextMenu, target)
1111     {
1112         if (!(target instanceof WebInspector.RemoteObject))
1113             return;
1114         var remoteObject = /** @type {WebInspector.RemoteObject} */ (target);
1115         if (remoteObject.type !== "function")
1116             return;
1117
1118         function didGetDetails(error, response)
1119         {
1120             if (error) {
1121                 console.error(error);
1122                 return;
1123             }
1124             WebInspector.inspectorView.showPanelForAnchorNavigation(this);
1125             var uiLocation = WebInspector.debuggerModel.rawLocationToUILocation(response.location);
1126             this._showSourceLine(uiLocation.uiSourceCode, uiLocation.lineNumber);
1127         }
1128
1129         function revealFunction()
1130         {
1131             DebuggerAgent.getFunctionDetails(remoteObject.objectId, didGetDetails.bind(this));
1132         }
1133
1134         contextMenu.appendItem(WebInspector.UIString("Show function definition"), revealFunction.bind(this));
1135     },
1136
1137     showGoToSourceDialog: function()
1138     {
1139         WebInspector.OpenResourceDialog.show(this, this._workspace, this.editorView.mainElement);
1140     },
1141
1142     __proto__: WebInspector.Panel.prototype
1143 }