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
26WebInspector.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
138WebInspector.DebuggerSidebarPanel.OffsetSectionsStyleClassName = "offset-sections";
139WebInspector.DebuggerSidebarPanel.ExceptionIconStyleClassName = "breakpoint-exception-icon";
140
141WebInspector.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey = "debugger-sidebar-panel-all-exceptions-breakpoint";
142WebInspector.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey = "debugger-sidebar-panel-all-uncaught-exceptions-breakpoint";
143
144WebInspector.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
590WebInspector.DebuggerSidebarPanel.prototype.__proto__ = WebInspector.NavigationSidebarPanel.prototype;
591