6b2a4f21ad98ddf22a7799e121111574ff12243b
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / DebuggerSidebarPanel.js
1 /*
2  * Copyright (C) 2013 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.DebuggerSidebarPanel = function()
27 {
28     WebInspector.NavigationSidebarPanel.call(this, "debugger", WebInspector.UIString("Debugger"), "Images/NavigationItemBug.svg", "3", true);
29
30     WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceChanged, this);
31     WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ResourceWasAdded, this._resourceAdded, this);
32
33     WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
34     WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.CallFramesDidChange, this._debuggerCallFramesDidChange, this);
35     WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this);
36     WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this);
37     WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this);
38     WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptsCleared, this._scriptsCleared, this);
39     WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
40     WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
41     WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange, this._debuggerActiveCallFrameDidChange, this);
42
43     this.pauseOrResumeKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Control | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, "Y", this._debuggerPauseResumeButtonClicked.bind(this));
44     this._stepOverKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F6, this._debuggerStepOverButtonClicked.bind(this));
45     this._stepIntoKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F7, this._debuggerStepIntoButtonClicked.bind(this));
46     this._stepOutKeyboardShortcut = new WebInspector.KeyboardShortcut(null, WebInspector.KeyboardShortcut.Key.F8, this._debuggerStepOutButtonClicked.bind(this));
47
48     this.pauseOrResumeAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Backslash, this._debuggerPauseResumeButtonClicked.bind(this));
49     this._stepOverAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.SingleQuote, this._debuggerStepOverButtonClicked.bind(this));
50     this._stepIntoAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Semicolon, this._debuggerStepIntoButtonClicked.bind(this));
51     this._stepOutAlternateKeyboardShortcut = new WebInspector.KeyboardShortcut(WebInspector.KeyboardShortcut.Modifier.Shift | WebInspector.KeyboardShortcut.Modifier.CommandOrControl, WebInspector.KeyboardShortcut.Key.Semicolon, this._debuggerStepOutButtonClicked.bind(this));
52
53     this._navigationBar = new WebInspector.NavigationBar;
54     this.element.appendChild(this._navigationBar.element);
55
56     var breakpointsImage, pauseImage, resumeImage, stepOverImage, stepIntoImage, stepOutImage;
57     if (WebInspector.Platform.isLegacyMacOS) {
58         breakpointsImage = {src: "Images/Legacy/Breakpoints.svg", width: 16, height: 16};
59         pauseImage = {src: "Images/Legacy/Pause.svg", width: 16, height: 16};
60         resumeImage = {src: "Images/Legacy/Resume.svg", width: 16, height: 16};
61         stepOverImage = {src: "Images/Legacy/StepOver.svg", width: 16, height: 16};
62         stepIntoImage = {src: "Images/Legacy/StepInto.svg", width: 16, height: 16};
63         stepOutImage = {src: "Images/Legacy/StepOut.svg", width: 16, height: 16};
64     } else {
65         breakpointsImage = {src: "Images/Breakpoints.svg", width: 15, height: 15};
66         pauseImage = {src: "Images/Pause.svg", width: 15, height: 15};
67         resumeImage = {src: "Images/Resume.svg", width: 15, height: 15};
68         stepOverImage = {src: "Images/StepOver.svg", width: 15, height: 15};
69         stepIntoImage = {src: "Images/StepInto.svg", width: 15, height: 15};
70         stepOutImage = {src: "Images/StepOut.svg", width: 15, height: 15};
71     }
72
73     var toolTip = WebInspector.UIString("Enable all breakpoints");
74     var altToolTip = WebInspector.UIString("Disable all breakpoints");
75
76     this._debuggerBreakpointsButtonItem = new WebInspector.ActivateButtonNavigationItem("debugger-breakpoints", toolTip, altToolTip, breakpointsImage.src, breakpointsImage.width, breakpointsImage.height);
77     this._debuggerBreakpointsButtonItem.activated = WebInspector.debuggerManager.breakpointsEnabled;
78     this._debuggerBreakpointsButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._breakpointsToggleButtonClicked, this);
79     this._navigationBar.addNavigationItem(this._debuggerBreakpointsButtonItem);
80
81     toolTip = WebInspector.UIString("Pause script execution (%s or %s)").format(this.pauseOrResumeKeyboardShortcut.displayName, this.pauseOrResumeAlternateKeyboardShortcut.displayName);
82     altToolTip = WebInspector.UIString("Continue script execution (%s or %s)").format(this.pauseOrResumeKeyboardShortcut.displayName, this.pauseOrResumeAlternateKeyboardShortcut.displayName);
83
84     this._debuggerPauseResumeButtonItem = new WebInspector.ToggleButtonNavigationItem("debugger-pause-resume", toolTip, altToolTip, pauseImage.src, resumeImage.src, pauseImage.width, pauseImage.height);
85     this._debuggerPauseResumeButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._debuggerPauseResumeButtonClicked, this);
86     this._navigationBar.addNavigationItem(this._debuggerPauseResumeButtonItem);
87
88     this._debuggerStepOverButtonItem = new WebInspector.ButtonNavigationItem("debugger-step-over", WebInspector.UIString("Step over (%s or %s)").format(this._stepOverKeyboardShortcut.displayName, this._stepOverAlternateKeyboardShortcut.displayName), stepOverImage.src, stepOverImage.width, stepOverImage.height);
89     this._debuggerStepOverButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._debuggerStepOverButtonClicked, this);
90     this._debuggerStepOverButtonItem.enabled = false;
91     this._navigationBar.addNavigationItem(this._debuggerStepOverButtonItem);
92
93     this._debuggerStepIntoButtonItem = new WebInspector.ButtonNavigationItem("debugger-step-into", WebInspector.UIString("Step into (%s or %s)").format(this._stepIntoKeyboardShortcut.displayName, this._stepIntoAlternateKeyboardShortcut.displayName), stepIntoImage.src, stepIntoImage.width, stepIntoImage.height);
94     this._debuggerStepIntoButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._debuggerStepIntoButtonClicked, this);
95     this._debuggerStepIntoButtonItem.enabled = false;
96     this._navigationBar.addNavigationItem(this._debuggerStepIntoButtonItem);
97
98     this._debuggerStepOutButtonItem = new WebInspector.ButtonNavigationItem("debugger-step-out", WebInspector.UIString("Step out (%s or %s)").format(this._stepOutKeyboardShortcut.displayName, this._stepOutAlternateKeyboardShortcut.displayName), stepOutImage.src, stepOutImage.width, stepOutImage.height);
99     this._debuggerStepOutButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._debuggerStepOutButtonClicked, this);
100     this._debuggerStepOutButtonItem.enabled = false;
101     this._navigationBar.addNavigationItem(this._debuggerStepOutButtonItem);
102
103     // Add this offset-sections class name so the sticky headers don't overlap the navigation bar.
104     this.element.classList.add(WebInspector.DebuggerSidebarPanel.OffsetSectionsStyleClassName);
105
106     this._allExceptionsBreakpointTreeElement = new WebInspector.BreakpointTreeElement(WebInspector.debuggerManager.allExceptionsBreakpoint, WebInspector.DebuggerSidebarPanel.ExceptionIconStyleClassName, WebInspector.UIString("All Exceptions"));
107     this._allUncaughtExceptionsBreakpointTreeElement = new WebInspector.BreakpointTreeElement(WebInspector.debuggerManager.allUncaughtExceptionsBreakpoint, WebInspector.DebuggerSidebarPanel.ExceptionIconStyleClassName, WebInspector.UIString("All Uncaught Exceptions"));
108
109     this.filterBar.placeholder = WebInspector.UIString("Filter Breakpoint List");
110
111     this._breakpointsContentTreeOutline = this.contentTreeOutline;
112     this._breakpointsContentTreeOutline.onselect = this._treeElementSelected.bind(this);
113     this._breakpointsContentTreeOutline.ondelete = this._breakpointTreeOutlineDeleteTreeElement.bind(this);
114     this._breakpointsContentTreeOutline.oncontextmenu = this._breakpointTreeOutlineContextMenuTreeElement.bind(this);
115
116     this._breakpointsContentTreeOutline.appendChild(this._allExceptionsBreakpointTreeElement);
117     this._breakpointsContentTreeOutline.appendChild(this._allUncaughtExceptionsBreakpointTreeElement);
118
119     var breakpointsRow = new WebInspector.DetailsSectionRow;
120     breakpointsRow.element.appendChild(this._breakpointsContentTreeOutline.element);
121
122     var breakpointsGroup = new WebInspector.DetailsSectionGroup([breakpointsRow]);
123     var breakpointsSection = new WebInspector.DetailsSection("breakpoints", WebInspector.UIString("Breakpoints"), [breakpointsGroup]);
124     this.contentElement.appendChild(breakpointsSection.element);
125
126     this._callStackContentTreeOutline = this.createContentTreeOutline(true);
127     this._callStackContentTreeOutline.onselect = this._treeElementSelected.bind(this);
128
129     this._callStackRow = new WebInspector.DetailsSectionRow(WebInspector.UIString("No Call Frames"));
130     this._callStackRow.showEmptyMessage();
131
132     var callStackGroup = new WebInspector.DetailsSectionGroup([this._callStackRow]);
133     this._callStackSection = new WebInspector.DetailsSection("call-stack", WebInspector.UIString("Call Stack"), [callStackGroup]);
134
135     WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisplayLocationDidChange, this._breakpointDisplayLocationDidChange, this);
136 };
137
138 WebInspector.DebuggerSidebarPanel.OffsetSectionsStyleClassName = "offset-sections";
139 WebInspector.DebuggerSidebarPanel.ExceptionIconStyleClassName = "breakpoint-exception-icon";
140
141 WebInspector.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey = "debugger-sidebar-panel-all-exceptions-breakpoint";
142 WebInspector.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey = "debugger-sidebar-panel-all-uncaught-exceptions-breakpoint";
143
144 WebInspector.DebuggerSidebarPanel.prototype = {
145     constructor: WebInspector.DebuggerSidebarPanel,
146
147     // Public
148
149     get hasSelectedElement()
150     {
151         return !!this._breakpointsContentTreeOutline.selectedTreeElement || !!this._callStackContentTreeOutline.selectedTreeElement;
152     },
153
154     showDefaultContentView: function()
155     {
156         WebInspector.resourceSidebarPanel.showDefaultContentView();
157     },
158
159     treeElementForRepresentedObject: function(representedObject)
160     {
161         // The main resource is used as the representedObject instead of Frame in our tree.
162         if (representedObject instanceof WebInspector.Frame)
163             representedObject = representedObject.mainResource;
164
165         return this.contentTreeOutline.getCachedTreeElement(representedObject);
166     },
167
168     // Protected
169
170     saveStateToCookie: function(cookie)
171     {
172         console.assert(cookie);
173
174         var selectedTreeElement = this._breakpointsContentTreeOutline.selectedTreeElement;
175         if (!selectedTreeElement)
176             return;
177
178         var representedObject = selectedTreeElement.representedObject;
179
180         if (representedObject === WebInspector.debuggerManager.allExceptionsBreakpoint)
181             cookie[WebInspector.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey] = true;
182
183         if (representedObject === WebInspector.debuggerManager.allUncaughtExceptionsBreakpoint)
184             cookie[WebInspector.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey] = true;
185
186         WebInspector.NavigationSidebarPanel.prototype.saveStateToCookie.call(this, cookie);
187     },
188
189     restoreStateFromCookie: function(cookie, relaxedMatchDelay)
190     {
191         console.assert(cookie);
192
193         // Eagerly resolve the special breakpoints; otherwise, use the default behavior.
194         if (cookie[WebInspector.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey])
195             this._allExceptionsBreakpointTreeElement.revealAndSelect();
196         else if (cookie[WebInspector.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey])
197             this._allUncaughtExceptionsBreakpointTreeElement.revealAndSelect();
198         else
199             WebInspector.NavigationSidebarPanel.prototype.restoreStateFromCookie.call(this, cookie, relaxedMatchDelay);
200     },
201
202     // Private
203
204     _debuggerPauseResumeButtonClicked: function(event)
205     {
206         if (WebInspector.debuggerManager.paused)
207             WebInspector.debuggerManager.resume();
208         else {
209             this._debuggerPauseResumeButtonItem.enabled = false;
210             WebInspector.debuggerManager.pause();
211         }
212     },
213
214     _debuggerStepOverButtonClicked: function(event)
215     {
216         WebInspector.debuggerManager.stepOver();
217     },
218
219     _debuggerStepIntoButtonClicked: function(event)
220     {
221         WebInspector.debuggerManager.stepInto();
222     },
223
224     _debuggerStepOutButtonClicked: function(event)
225     {
226         WebInspector.debuggerManager.stepOut();
227     },
228
229     _debuggerDidPause: function(event)
230     {
231         this.contentElement.insertBefore(this._callStackSection.element, this.contentElement.firstChild);
232
233         this._debuggerPauseResumeButtonItem.enabled = true;
234         this._debuggerPauseResumeButtonItem.toggled = true;
235         this._debuggerStepOverButtonItem.enabled = true;
236         this._debuggerStepIntoButtonItem.enabled = true;
237     },
238
239     _debuggerDidResume: function(event)
240     {
241         this._callStackSection.element.remove();
242
243         this._debuggerPauseResumeButtonItem.enabled = true;
244         this._debuggerPauseResumeButtonItem.toggled = false;
245         this._debuggerStepOverButtonItem.enabled = false;
246         this._debuggerStepIntoButtonItem.enabled = false;
247         this._debuggerStepOutButtonItem.enabled = false;
248     },
249
250     _breakpointsEnabledDidChange: function(event)
251     {
252         this._debuggerBreakpointsButtonItem.activated = WebInspector.debuggerManager.breakpointsEnabled;
253     },
254
255     _breakpointsToggleButtonClicked: function(event)
256     {
257         WebInspector.debuggerManager.breakpointsEnabled = !this._debuggerBreakpointsButtonItem.activated;
258     },
259
260     _addBreakpoint: function(breakpoint, sourceCode)
261     {
262         var sourceCode = breakpoint.sourceCodeLocation.displaySourceCode;
263         if (!sourceCode)
264             return null;
265
266         var parentTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(sourceCode);
267         if (!parentTreeElement) {
268             if (sourceCode instanceof WebInspector.SourceMapResource)
269                 parentTreeElement = new WebInspector.SourceMapResourceTreeElement(sourceCode);
270             else if (sourceCode instanceof WebInspector.Resource)
271                 parentTreeElement = new WebInspector.ResourceTreeElement(sourceCode);
272             else if (sourceCode instanceof WebInspector.Script)
273                 parentTreeElement = new WebInspector.ScriptTreeElement(sourceCode);
274         }
275
276         if (!parentTreeElement.parent) {
277             parentTreeElement.hasChildren = true;
278             parentTreeElement.expand();
279
280             this._breakpointsContentTreeOutline.insertChild(parentTreeElement, insertionIndexForObjectInListSortedByFunction(parentTreeElement, this._breakpointsContentTreeOutline.children, this._compareTopLevelTreeElements.bind(this)));
281         }
282
283         // Mark disabled breakpoints as resolved if there is source code loaded with that URL.
284         // This gives the illusion the breakpoint was resolved, but since we don't send disabled
285         // breakpoints to the backend we don't know for sure. If the user enables the breakpoint
286         // it will be resolved properly.
287         if (breakpoint.disabled)
288             breakpoint.resolved = true;
289
290         var breakpointTreeElement = new WebInspector.BreakpointTreeElement(breakpoint);
291         parentTreeElement.insertChild(breakpointTreeElement, insertionIndexForObjectInListSortedByFunction(breakpointTreeElement, parentTreeElement.children, this._compareBreakpointTreeElements));
292         return breakpointTreeElement;
293     },
294
295     _addBreakpointsForSourceCode: function(sourceCode)
296     {
297         var breakpoints = WebInspector.debuggerManager.breakpointsForSourceCode(sourceCode);
298         for (var i = 0; i < breakpoints.length; ++i)
299             this._addBreakpoint(breakpoints[i], sourceCode);
300     },
301
302     _resourceAdded: function(event)
303     {
304         var resource = event.data.resource;
305         this._addBreakpointsForSourceCode(resource);
306     },
307
308     _mainResourceChanged: function(event)
309     {
310         var resource = event.target.mainResource;
311         this._addBreakpointsForSourceCode(resource);
312     },
313
314     _scriptAdded: function(event)
315     {
316         var script = event.data.script;
317
318         // Don't add breakpoints if the script is represented by a Resource. They were
319         // already added by _resourceAdded.
320         if (script.resource)
321             return;
322
323         this._addBreakpointsForSourceCode(script);
324     },
325
326     _scriptsCleared: function(event)
327     {
328         for (var i = this._breakpointsContentTreeOutline.children.length - 1; i >= 0; --i) {
329             var treeElement = this._breakpointsContentTreeOutline.children[i];
330             if (!(treeElement instanceof WebInspector.ScriptTreeElement))
331                 continue;
332
333             this._breakpointsContentTreeOutline.removeChildAtIndex(i, true, true);
334         }
335     },
336
337     _breakpointAdded: function(event)
338     {
339         var breakpoint = event.data.breakpoint;
340         this._addBreakpoint(breakpoint);
341     },
342
343     _breakpointRemoved: function(event)
344     {
345         var breakpoint = event.data.breakpoint;
346
347         var breakpointTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(breakpoint);
348         console.assert(breakpointTreeElement);
349         if (!breakpointTreeElement)
350             return;
351
352         this._removeBreakpointTreeElement(breakpointTreeElement);
353     },
354
355     _breakpointDisplayLocationDidChange: function(event)
356     {
357         var breakpoint = event.target;
358         if (event.data.oldDisplaySourceCode === breakpoint.displaySourceCode)
359             return;
360
361         var breakpointTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(breakpoint);
362         if (!breakpointTreeElement)
363             return;
364
365         // A known breakpoint moved between resources, remove the old tree element
366         // and create a new tree element with the updated file.
367
368         var wasSelected = breakpointTreeElement.selected;
369
370         this._removeBreakpointTreeElement(breakpointTreeElement);
371         var newBreakpointTreeElement = this._addBreakpoint(breakpoint);
372
373         if (newBreakpointTreeElement && wasSelected)
374             newBreakpointTreeElement.revealAndSelect(true, false, true, true);
375     },
376
377     _removeBreakpointTreeElement: function(breakpointTreeElement)
378     {
379         var parentTreeElement = breakpointTreeElement.parent;
380         parentTreeElement.removeChild(breakpointTreeElement);
381
382         console.assert(parentTreeElement.parent === this._breakpointsContentTreeOutline);
383
384         if (!parentTreeElement.children.length)
385             this._breakpointsContentTreeOutline.removeChild(parentTreeElement);
386     },
387
388     _debuggerCallFramesDidChange: function()
389     {
390         this._callStackContentTreeOutline.removeChildren();
391
392         var callFrames = WebInspector.debuggerManager.callFrames;
393         if (!callFrames || !callFrames.length) {
394             this._callStackRow.showEmptyMessage();
395             return;
396         }
397
398         this._callStackRow.hideEmptyMessage();
399         this._callStackRow.element.appendChild(this._callStackContentTreeOutline.element);
400
401         var treeElementToSelect = null;
402
403         var activeCallFrame = WebInspector.debuggerManager.activeCallFrame;
404         for (var i = 0; i < callFrames.length; ++i) {
405             var callFrameTreeElement = new WebInspector.CallFrameTreeElement(callFrames[i]);
406             if (callFrames[i] === activeCallFrame)
407                 treeElementToSelect = callFrameTreeElement;
408             this._callStackContentTreeOutline.appendChild(callFrameTreeElement);
409         }
410
411         if (treeElementToSelect)
412             treeElementToSelect.select(true, true);
413     },
414
415     _debuggerActiveCallFrameDidChange: function()
416     {
417         var callFrames = WebInspector.debuggerManager.callFrames;
418         if (!callFrames)
419             return;
420
421         var indexOfActiveCallFrame = callFrames.indexOf(WebInspector.debuggerManager.activeCallFrame);
422         // It is useful to turn off the step out button when there is no call frame to go through
423         // since there might be call frames in the backend that were removed when processing the call
424         // frame payload.
425         this._debuggerStepOutButtonItem.enabled = indexOfActiveCallFrame < callFrames.length - 1;
426     },
427
428     _breakpointsBeneathTreeElement: function(treeElement)
429     {
430         console.assert(treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement);
431         if (!(treeElement instanceof WebInspector.ResourceTreeElement) && !(treeElement instanceof WebInspector.ScriptTreeElement))
432             return [];
433
434         var breakpoints = [];
435         var breakpointTreeElements = treeElement.children;
436         for (var i = 0; i < breakpointTreeElements.length; ++i) {
437             console.assert(breakpointTreeElements[i] instanceof WebInspector.BreakpointTreeElement);
438             console.assert(breakpointTreeElements[i].breakpoint);
439             var breakpoint = breakpointTreeElements[i].breakpoint;
440             if (breakpoint)
441                 breakpoints.push(breakpoint);
442         }
443
444         return breakpoints;
445     },
446
447     _removeAllBreakpoints: function(breakpoints)
448     {
449         for (var i = 0; i < breakpoints.length; ++i) {
450             var breakpoint = breakpoints[i];
451             if (WebInspector.debuggerManager.isBreakpointRemovable(breakpoint))
452                 WebInspector.debuggerManager.removeBreakpoint(breakpoint);
453         }
454     },
455
456     _toggleAllBreakpoints: function(breakpoints, disabled)
457     {
458         for (var i = 0; i < breakpoints.length; ++i)
459             breakpoints[i].disabled = disabled;
460     },
461
462     _breakpointTreeOutlineDeleteTreeElement: function(treeElement)
463     {
464         console.assert(treeElement.selected);
465         console.assert(treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement);
466         if (!(treeElement instanceof WebInspector.ResourceTreeElement) && !(treeElement instanceof WebInspector.ScriptTreeElement))
467             return false;
468
469         var wasTopResourceTreeElement = treeElement.previousSibling === this._allUncaughtExceptionsBreakpointTreeElement;
470         var nextSibling = treeElement.nextSibling;
471
472         var breakpoints = this._breakpointsBeneathTreeElement(treeElement);
473         this._removeAllBreakpoints(breakpoints);
474
475         if (wasTopResourceTreeElement && nextSibling)
476             nextSibling.select(true, true);
477
478         return true;
479     },
480
481     _breakpointTreeOutlineContextMenuTreeElement: function(event, treeElement)
482     {
483         console.assert(treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement);
484         if (!(treeElement instanceof WebInspector.ResourceTreeElement) && !(treeElement instanceof WebInspector.ScriptTreeElement))
485             return;
486
487         var breakpoints = this._breakpointsBeneathTreeElement(treeElement);
488         var shouldDisable = false;
489         for (var i = 0; i < breakpoints.length; ++i) {
490             if (!breakpoints[i].disabled) {
491                 shouldDisable = true;
492                 break;
493             }
494         }
495
496         function removeAllResourceBreakpoints()
497         {
498             this._removeAllBreakpoints(breakpoints);
499         }
500
501         function toggleAllResourceBreakpoints()
502         {
503             this._toggleAllBreakpoints(breakpoints, shouldDisable);
504         }
505
506         var contextMenu = new WebInspector.ContextMenu(event);
507         if (shouldDisable)
508             contextMenu.appendItem(WebInspector.UIString("Disable Breakpoints"), toggleAllResourceBreakpoints.bind(this));
509         else
510             contextMenu.appendItem(WebInspector.UIString("Enable Breakpoints"), toggleAllResourceBreakpoints.bind(this));
511         contextMenu.appendItem(WebInspector.UIString("Delete Breakpoints"), removeAllResourceBreakpoints.bind(this));
512         contextMenu.show();
513     },
514
515     _treeElementSelected: function(treeElement, selectedByUser)
516     {
517         function deselectCallStackContentTreeElements()
518         {
519             // Deselect any tree element in the call stack content tree outline to prevent two selections in the sidebar.
520             var selectedTreeElement = this._callStackContentTreeOutline.selectedTreeElement;
521             if (selectedTreeElement)
522                 selectedTreeElement.deselect();
523         }
524
525         if (treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement) {
526             // If the resource is being selected when it has no children it is in the process of being deleted, don't do anything.
527             if (!treeElement.children.length)
528                 return;
529             deselectCallStackContentTreeElements.call(this);
530             WebInspector.resourceSidebarPanel.showSourceCode(treeElement.representedObject);
531             return;
532         }
533
534         if (treeElement instanceof WebInspector.CallFrameTreeElement) {
535             // Deselect any tree element in the breakpoints content tree outline to prevent two selections in the sidebar.
536             var selectedTreeElement = this._breakpointsContentTreeOutline.selectedTreeElement;
537             if (selectedTreeElement)
538                 selectedTreeElement.deselect();
539
540             var callFrame = treeElement.callFrame;
541             WebInspector.debuggerManager.activeCallFrame = callFrame;
542             WebInspector.resourceSidebarPanel.showSourceCodeLocation(callFrame.sourceCodeLocation);
543             return;
544         }
545
546         if (!(treeElement instanceof WebInspector.BreakpointTreeElement))
547             return;
548
549         deselectCallStackContentTreeElements.call(this);
550
551         if (!treeElement.parent.representedObject)
552             return;
553
554         console.assert(treeElement.parent.representedObject instanceof WebInspector.SourceCode);
555         if (!(treeElement.parent.representedObject instanceof WebInspector.SourceCode))
556             return;
557
558         var breakpoint = treeElement.breakpoint;
559         WebInspector.resourceSidebarPanel.showSourceCodeLocation(breakpoint.sourceCodeLocation);
560     },
561
562     _compareTopLevelTreeElements: function(a, b)
563     {
564         if (a === this._allExceptionsBreakpointTreeElement)
565             return -1;
566         if (b === this._allExceptionsBreakpointTreeElement)
567             return 1;
568
569         if (a === this._allUncaughtExceptionsBreakpointTreeElement)
570             return -1;
571         if (b === this._allUncaughtExceptionsBreakpointTreeElement)
572             return 1;
573
574         return a.mainTitle.localeCompare(b.mainTitle);
575     },
576
577     _compareBreakpointTreeElements: function(a, b)
578     {
579         var aLocation = a.breakpoint.sourceCodeLocation;
580         var bLocation = b.breakpoint.sourceCodeLocation;
581
582         var comparisonResult = aLocation.displayLineNumber - bLocation.displayLineNumber;
583         if (comparisonResult !== 0)
584             return comparisonResult;
585
586         return aLocation.displayColumnNumber - bLocation.displayColumnNumber;
587     }
588 };
589
590 WebInspector.DebuggerSidebarPanel.prototype.__proto__ = WebInspector.NavigationSidebarPanel.prototype;