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