Web Inspector: "No Filter Results" message overlaps Debugger sidebar sections
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / DebuggerSidebarPanel.js
1 /*
2  * Copyright (C) 2013, 2015 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 = class DebuggerSidebarPanel extends WebInspector.NavigationSidebarPanel
27 {
28     constructor(contentBrowser)
29     {
30         super("debugger", WebInspector.UIString("Debugger"), true);
31
32         this.contentBrowser = contentBrowser;
33
34         WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
35         WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ResourceWasAdded, this._resourceAdded, this);
36
37         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
38         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.CallFramesDidChange, this._debuggerCallFramesDidChange, this);
39         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this);
40         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this);
41         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this);
42         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptRemoved, this._scriptRemoved, this);
43         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptsCleared, this._scriptsCleared, this);
44         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
45         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
46         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange, this._debuggerActiveCallFrameDidChange, this);
47         WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.WaitingToPause, this._debuggerWaitingToPause, this);
48
49         this._navigationBar = new WebInspector.NavigationBar;
50         this.addSubview(this._navigationBar);
51
52         var breakpointsImage = {src: "Images/Breakpoints.svg", width: 15, height: 15};
53         var pauseImage = {src: "Images/Pause.svg", width: 15, height: 15};
54         var resumeImage = {src: "Images/Resume.svg", width: 15, height: 15};
55         var stepOverImage = {src: "Images/StepOver.svg", width: 15, height: 15};
56         var stepIntoImage = {src: "Images/StepInto.svg", width: 15, height: 15};
57         var stepOutImage = {src: "Images/StepOut.svg", width: 15, height: 15};
58
59         var toolTip = WebInspector.UIString("Enable all breakpoints (%s)").format(WebInspector.toggleBreakpointsKeyboardShortcut.displayName);
60         var altToolTip = WebInspector.UIString("Disable all breakpoints (%s)").format(WebInspector.toggleBreakpointsKeyboardShortcut.displayName);
61
62         this._debuggerBreakpointsButtonItem = new WebInspector.ActivateButtonNavigationItem("debugger-breakpoints", toolTip, altToolTip, breakpointsImage.src, breakpointsImage.width, breakpointsImage.height);
63         this._debuggerBreakpointsButtonItem.activated = WebInspector.debuggerManager.breakpointsEnabled;
64         this._debuggerBreakpointsButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, WebInspector.debuggerToggleBreakpoints, this);
65         this._navigationBar.addNavigationItem(this._debuggerBreakpointsButtonItem);
66
67         toolTip = WebInspector.UIString("Pause script execution (%s or %s)").format(WebInspector.pauseOrResumeKeyboardShortcut.displayName, WebInspector.pauseOrResumeAlternateKeyboardShortcut.displayName);
68         altToolTip = WebInspector.UIString("Continue script execution (%s or %s)").format(WebInspector.pauseOrResumeKeyboardShortcut.displayName, WebInspector.pauseOrResumeAlternateKeyboardShortcut.displayName);
69
70         this._debuggerPauseResumeButtonItem = new WebInspector.ToggleButtonNavigationItem("debugger-pause-resume", toolTip, altToolTip, pauseImage.src, resumeImage.src, pauseImage.width, pauseImage.height);
71         this._debuggerPauseResumeButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, WebInspector.debuggerPauseResumeToggle, this);
72         this._navigationBar.addNavigationItem(this._debuggerPauseResumeButtonItem);
73
74         this._debuggerStepOverButtonItem = new WebInspector.ButtonNavigationItem("debugger-step-over", WebInspector.UIString("Step over (%s or %s)").format(WebInspector.stepOverKeyboardShortcut.displayName, WebInspector.stepOverAlternateKeyboardShortcut.displayName), stepOverImage.src, stepOverImage.width, stepOverImage.height);
75         this._debuggerStepOverButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, WebInspector.debuggerStepOver, this);
76         this._debuggerStepOverButtonItem.enabled = false;
77         this._navigationBar.addNavigationItem(this._debuggerStepOverButtonItem);
78
79         this._debuggerStepIntoButtonItem = new WebInspector.ButtonNavigationItem("debugger-step-into", WebInspector.UIString("Step into (%s or %s)").format(WebInspector.stepIntoKeyboardShortcut.displayName, WebInspector.stepIntoAlternateKeyboardShortcut.displayName), stepIntoImage.src, stepIntoImage.width, stepIntoImage.height);
80         this._debuggerStepIntoButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, WebInspector.debuggerStepInto, this);
81         this._debuggerStepIntoButtonItem.enabled = false;
82         this._navigationBar.addNavigationItem(this._debuggerStepIntoButtonItem);
83
84         this._debuggerStepOutButtonItem = new WebInspector.ButtonNavigationItem("debugger-step-out", WebInspector.UIString("Step out (%s or %s)").format(WebInspector.stepOutKeyboardShortcut.displayName, WebInspector.stepOutAlternateKeyboardShortcut.displayName), stepOutImage.src, stepOutImage.width, stepOutImage.height);
85         this._debuggerStepOutButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, WebInspector.debuggerStepOut, this);
86         this._debuggerStepOutButtonItem.enabled = false;
87         this._navigationBar.addNavigationItem(this._debuggerStepOutButtonItem);
88
89         // Add this offset-sections class name so the sticky headers don't overlap the navigation bar.
90         this.element.classList.add(WebInspector.DebuggerSidebarPanel.OffsetSectionsStyleClassName);
91
92         this._globalBreakpointsFolderTreeElement = new WebInspector.FolderTreeElement(WebInspector.UIString("Global Breakpoints"), null, WebInspector.DebuggerSidebarPanel.GlobalIconStyleClassName);
93         this._allExceptionsBreakpointTreeElement = new WebInspector.BreakpointTreeElement(WebInspector.debuggerManager.allExceptionsBreakpoint, WebInspector.DebuggerSidebarPanel.ExceptionIconStyleClassName, WebInspector.UIString("All Exceptions"));
94         this._allUncaughtExceptionsBreakpointTreeElement = new WebInspector.BreakpointTreeElement(WebInspector.debuggerManager.allUncaughtExceptionsBreakpoint, WebInspector.DebuggerSidebarPanel.ExceptionIconStyleClassName, WebInspector.UIString("All Uncaught Exceptions"));
95         this.suppressFilteringOnTreeElements([this._globalBreakpointsFolderTreeElement, this._allExceptionsBreakpointTreeElement, this._allUncaughtExceptionsBreakpointTreeElement]);
96
97         this.filterBar.placeholder = WebInspector.UIString("Filter Breakpoint List");
98
99         function showResourcesWithIssuesOnlyFilterFunction(treeElement)
100         {
101             // Keep issues.
102             if (treeElement instanceof WebInspector.IssueTreeElement)
103                 return true;
104
105             // Keep resources with issues.
106             if (treeElement.hasChildren) {
107                 for (let child of treeElement.children) {
108                     if (child instanceof WebInspector.IssueTreeElement)
109                         return true;
110                 }
111             }
112             return false;
113         };
114
115         this.filterBar.addFilterBarButton("debugger-show-resources-with-issues-only", showResourcesWithIssuesOnlyFilterFunction, true, WebInspector.UIString("Show only resources with issues."), WebInspector.UIString("Show resources with and without issues."), "Images/Errors.svg", 15, 15);
116
117         this._breakpointsContentTreeOutline = this.contentTreeOutline;
118
119         let breakpointsRow = new WebInspector.DetailsSectionRow;
120         breakpointsRow.element.appendChild(this._breakpointsContentTreeOutline.element);
121
122         let breakpointsGroup = new WebInspector.DetailsSectionGroup([breakpointsRow]);
123         let breakpointsSection = new WebInspector.DetailsSection("scripts", WebInspector.UIString("Scripts"), [breakpointsGroup]);
124         this.contentView.element.appendChild(breakpointsSection.element);
125
126         this._breakpointsContentTreeOutline.element.classList.add("breakpoints");
127         this._breakpointsContentTreeOutline.addEventListener(WebInspector.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
128         this._breakpointsContentTreeOutline.ondelete = this._breakpointTreeOutlineDeleteTreeElement.bind(this);
129         this._breakpointsContentTreeOutline.oncontextmenu = this._breakpointTreeOutlineContextMenuTreeElement.bind(this);
130
131         this._breakpointsContentTreeOutline.appendChild(this._globalBreakpointsFolderTreeElement);
132         this._globalBreakpointsFolderTreeElement.appendChild(this._allExceptionsBreakpointTreeElement);
133         this._globalBreakpointsFolderTreeElement.appendChild(this._allUncaughtExceptionsBreakpointTreeElement);
134         this._globalBreakpointsFolderTreeElement.expand();
135
136         this._callStackContentTreeOutline = this.createContentTreeOutline(true, true);
137         this._callStackContentTreeOutline.addEventListener(WebInspector.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
138
139         this._callStackRow = new WebInspector.DetailsSectionRow(WebInspector.UIString("No Call Frames"));
140         this._callStackRow.showEmptyMessage();
141
142         let callStackGroup = new WebInspector.DetailsSectionGroup([this._callStackRow]);
143         this._callStackSection = new WebInspector.DetailsSection("call-stack", WebInspector.UIString("Call Stack"), [callStackGroup]);
144
145         this._pauseReasonTreeOutline = null;
146
147         this._pauseReasonLinkContainerElement = document.createElement("span");
148         this._pauseReasonTextRow = new WebInspector.DetailsSectionTextRow;
149         this._pauseReasonGroup = new WebInspector.DetailsSectionGroup([this._pauseReasonTextRow]);
150         this._pauseReasonSection = new WebInspector.DetailsSection("paused-reason", null, [this._pauseReasonGroup], this._pauseReasonLinkContainerElement);
151         this._pauseReasonSection.title = WebInspector.UIString("Pause Reason");
152
153         WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
154         WebInspector.IssueMessage.addEventListener(WebInspector.IssueMessage.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
155         WebInspector.issueManager.addEventListener(WebInspector.IssueManager.Event.IssueWasAdded, this._handleIssueAdded, this);
156         WebInspector.issueManager.addEventListener(WebInspector.IssueManager.Event.Cleared, this._handleIssuesCleared, this);
157
158         if (WebInspector.frameResourceManager.mainFrame)
159             this._addResourcesRecursivelyForFrame(WebInspector.frameResourceManager.mainFrame);
160
161         for (var script of WebInspector.debuggerManager.knownNonResourceScripts)
162             this._addScript(script);
163     }
164
165     // Public
166
167     closed()
168     {
169         super.closed();
170
171         WebInspector.Frame.removeEventListener(null, null, this);
172         WebInspector.debuggerManager.removeEventListener(null, null, this);
173         WebInspector.Breakpoint.removeEventListener(null, null, this);
174         WebInspector.IssueMessage.removeEventListener(null, null, this);
175     }
176
177     showDefaultContentView()
178     {
179         var currentTreeElement = this._contentTreeOutline.children[0];
180         while (currentTreeElement && !currentTreeElement.root) {
181             if (currentTreeElement instanceof WebInspector.ResourceTreeElement || currentTreeElement instanceof WebInspector.ScriptTreeElement) {
182                 this.showDefaultContentViewForTreeElement(currentTreeElement);
183                 return;
184             }
185
186             currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true);
187         }
188     }
189
190     treeElementForRepresentedObject(representedObject)
191     {
192         // The main resource is used as the representedObject instead of Frame in our tree.
193         if (representedObject instanceof WebInspector.Frame)
194             representedObject = representedObject.mainResource;
195
196         return this.contentTreeOutline.getCachedTreeElement(representedObject);
197     }
198
199     // Protected
200
201     saveStateToCookie(cookie)
202     {
203         console.assert(cookie);
204
205         var selectedTreeElement = this._breakpointsContentTreeOutline.selectedTreeElement;
206         if (!selectedTreeElement)
207             return;
208
209         var representedObject = selectedTreeElement.representedObject;
210
211         if (representedObject === WebInspector.debuggerManager.allExceptionsBreakpoint) {
212             cookie[WebInspector.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey] = true;
213             return;
214         }
215
216         if (representedObject === WebInspector.debuggerManager.allUncaughtExceptionsBreakpoint) {
217             cookie[WebInspector.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey] = true;
218             return;
219         }
220
221         super.saveStateToCookie(cookie);
222     }
223
224     restoreStateFromCookie(cookie, relaxedMatchDelay)
225     {
226         console.assert(cookie);
227
228         // Eagerly resolve the special breakpoints; otherwise, use the default behavior.
229         if (cookie[WebInspector.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey])
230             this._allExceptionsBreakpointTreeElement.revealAndSelect();
231         else if (cookie[WebInspector.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey])
232             this._allUncaughtExceptionsBreakpointTreeElement.revealAndSelect();
233         else
234             super.restoreStateFromCookie(cookie, relaxedMatchDelay);
235     }
236
237     // Private
238
239     _debuggerWaitingToPause(event)
240     {
241         this._debuggerPauseResumeButtonItem.enabled = false;
242     }
243
244     _debuggerDidPause(event)
245     {
246         this.contentView.element.insertBefore(this._callStackSection.element, this.contentView.element.firstChild);
247         if (this._updatePauseReason())
248             this.contentView.element.insertBefore(this._pauseReasonSection.element, this.contentView.element.firstChild);
249
250         this._debuggerPauseResumeButtonItem.enabled = true;
251         this._debuggerPauseResumeButtonItem.toggled = true;
252         this._debuggerStepOverButtonItem.enabled = true;
253         this._debuggerStepIntoButtonItem.enabled = true;
254
255         this.element.classList.add(WebInspector.DebuggerSidebarPanel.DebuggerPausedStyleClassName);
256     }
257
258     _debuggerDidResume(event)
259     {
260         this._callStackSection.element.remove();
261         this._pauseReasonSection.element.remove();
262
263         this._debuggerPauseResumeButtonItem.enabled = true;
264         this._debuggerPauseResumeButtonItem.toggled = false;
265         this._debuggerStepOverButtonItem.enabled = false;
266         this._debuggerStepIntoButtonItem.enabled = false;
267         this._debuggerStepOutButtonItem.enabled = false;
268
269         this.element.classList.remove(WebInspector.DebuggerSidebarPanel.DebuggerPausedStyleClassName);
270     }
271
272     _breakpointsEnabledDidChange(event)
273     {
274         this._debuggerBreakpointsButtonItem.activated = WebInspector.debuggerManager.breakpointsEnabled;
275     }
276
277     _addBreakpoint(breakpoint)
278     {
279         var sourceCode = breakpoint.sourceCodeLocation.displaySourceCode;
280         if (!sourceCode)
281             return null;
282
283         var parentTreeElement = this._addTreeElementForSourceCodeToContentTreeOutline(sourceCode);
284
285         // Mark disabled breakpoints as resolved if there is source code loaded with that URL.
286         // This gives the illusion the breakpoint was resolved, but since we don't send disabled
287         // breakpoints to the backend we don't know for sure. If the user enables the breakpoint
288         // it will be resolved properly.
289         if (breakpoint.disabled)
290             breakpoint.resolved = true;
291
292         var breakpointTreeElement = new WebInspector.BreakpointTreeElement(breakpoint);
293         parentTreeElement.insertChild(breakpointTreeElement, insertionIndexForObjectInListSortedByFunction(breakpointTreeElement, parentTreeElement.children, this._compareDebuggerTreeElements));
294         if (parentTreeElement.children.length === 1)
295             parentTreeElement.expand();
296         return breakpointTreeElement;
297     }
298
299     _addBreakpointsForSourceCode(sourceCode)
300     {
301         var breakpoints = WebInspector.debuggerManager.breakpointsForSourceCode(sourceCode);
302         for (var i = 0; i < breakpoints.length; ++i)
303             this._addBreakpoint(breakpoints[i], sourceCode);
304     }
305
306     _addIssuesForSourceCode(sourceCode)
307     {
308         var issues = WebInspector.issueManager.issuesForSourceCode(sourceCode);
309         for (var issue of issues)
310             this._addIssue(issue);
311     }
312
313     _addTreeElementForSourceCodeToContentTreeOutline(sourceCode)
314     {
315         var treeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(sourceCode);
316         if (!treeElement) {
317             if (sourceCode instanceof WebInspector.SourceMapResource)
318                 treeElement = new WebInspector.SourceMapResourceTreeElement(sourceCode);
319             else if (sourceCode instanceof WebInspector.Resource)
320                 treeElement = new WebInspector.ResourceTreeElement(sourceCode);
321             else if (sourceCode instanceof WebInspector.Script)
322                 treeElement = new WebInspector.ScriptTreeElement(sourceCode);
323         }
324
325         if (!treeElement) {
326             console.error("Unknown sourceCode instance", sourceCode);
327             return;
328         }
329
330         if (!treeElement.parent) {
331             treeElement.hasChildren = false;
332             treeElement.expand();
333
334             this._breakpointsContentTreeOutline.insertChild(treeElement, insertionIndexForObjectInListSortedByFunction(treeElement, this._breakpointsContentTreeOutline.children, this._compareTopLevelTreeElements.bind(this)));
335         }
336
337         return treeElement;
338     }
339
340     _addResourcesRecursivelyForFrame(frame)
341     {
342         this._addResource(frame.mainResource);
343
344         for (var resource of frame.resources)
345             this._addResource(resource);
346
347         for (var childFrame of frame.childFrames)
348             this._addResourcesRecursivelyForFrame(childFrame);
349     }
350
351     _resourceAdded(event)
352     {
353         this._addResource(event.data.resource);
354     }
355
356     _addResource(resource)
357     {
358         if (![WebInspector.Resource.Type.Document, WebInspector.Resource.Type.Script].includes(resource.type))
359             return;
360
361         let treeElement = this._addTreeElementForSourceCodeToContentTreeOutline(resource);
362         this._addBreakpointsForSourceCode(resource);
363         this._addIssuesForSourceCode(resource);
364
365         if (!this.contentBrowser.currentContentView)
366             this.showDefaultContentViewForTreeElement(treeElement);
367     }
368
369     _mainResourceDidChange(event)
370     {
371         if (event.target.isMainFrame()) {
372             // Aggressively prune resources now so the old resources are removed before
373             // the new main resource is added below. This avoids a visual flash when the
374             // prune normally happens on a later event loop cycle.
375             this.pruneStaleResourceTreeElements();
376             this.contentBrowser.contentViewContainer.closeAllContentViews();
377         }
378
379         var resource = event.target.mainResource;
380         this._addTreeElementForSourceCodeToContentTreeOutline(resource);
381         this._addBreakpointsForSourceCode(resource);
382         this._addIssuesForSourceCode(resource);
383     }
384
385     _scriptAdded(event)
386     {
387         this._addScript(event.data.script);
388     }
389
390     _addScript(script)
391     {
392         // COMPATIBILITY(iOS 9): Backends could send the frontend built-in code, filter out JSC internals.
393         if (!script.url)
394             return;
395
396         // Don't add breakpoints if the script is represented by a Resource. They were
397         // already added by _resourceAdded.
398         if (script.resource)
399             return;
400
401         let treeElement = this._addTreeElementForSourceCodeToContentTreeOutline(script);
402         this._addBreakpointsForSourceCode(script);
403         this._addIssuesForSourceCode(script);
404
405         if (!this.contentBrowser.currentContentView)
406             this.showDefaultContentViewForTreeElement(treeElement);
407     }
408
409     _scriptRemoved(event)
410     {
411         let script = event.data.script;
412         let scriptTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(script);
413         if (!scriptTreeElement)
414             return;
415
416         scriptTreeElement.parent.removeChild(scriptTreeElement);
417     }
418
419     _scriptsCleared(event)
420     {
421         for (var i = this._breakpointsContentTreeOutline.children.length - 1; i >= 0; --i) {
422             var treeElement = this._breakpointsContentTreeOutline.children[i];
423             if (!(treeElement instanceof WebInspector.ScriptTreeElement))
424                 continue;
425
426             this._breakpointsContentTreeOutline.removeChildAtIndex(i, true, true);
427         }
428     }
429
430     _breakpointAdded(event)
431     {
432         var breakpoint = event.data.breakpoint;
433         this._addBreakpoint(breakpoint);
434     }
435
436     _breakpointRemoved(event)
437     {
438         var breakpoint = event.data.breakpoint;
439
440         if (this._pauseReasonTreeOutline) {
441             var pauseReasonBreakpointTreeElement = this._pauseReasonTreeOutline.getCachedTreeElement(breakpoint);
442             if (pauseReasonBreakpointTreeElement)
443                 pauseReasonBreakpointTreeElement.removeStatusImage();
444         }
445
446         var breakpointTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(breakpoint);
447         console.assert(breakpointTreeElement);
448         if (!breakpointTreeElement)
449             return;
450
451         this._removeDebuggerTreeElement(breakpointTreeElement);
452     }
453
454     _handleDebuggerObjectDisplayLocationDidChange(event)
455     {
456         var debuggerObject = event.target;
457
458         if (event.data.oldDisplaySourceCode === debuggerObject.sourceCodeLocation.displaySourceCode)
459             return;
460
461         var debuggerTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(debuggerObject);
462         if (!debuggerTreeElement)
463             return;
464
465         // A known debugger object (breakpoint, issueMessage, etc.) moved between resources, remove the old tree element
466         // and create a new tree element with the updated file.
467
468         var wasSelected = debuggerTreeElement.selected;
469
470         this._removeDebuggerTreeElement(debuggerTreeElement);
471         var newDebuggerTreeElement = this._addDebuggerObject(debuggerObject);
472
473         if (newDebuggerTreeElement && wasSelected)
474             newDebuggerTreeElement.revealAndSelect(true, false, true, true);
475     }
476
477     _removeDebuggerTreeElement(debuggerTreeElement)
478     {
479         var parentTreeElement = debuggerTreeElement.parent;
480         parentTreeElement.removeChild(debuggerTreeElement);
481
482         console.assert(parentTreeElement.parent === this._breakpointsContentTreeOutline);
483     }
484
485     _debuggerCallFramesDidChange()
486     {
487         this._callStackContentTreeOutline.removeChildren();
488
489         var callFrames = WebInspector.debuggerManager.callFrames;
490         if (!callFrames || !callFrames.length) {
491             this._callStackRow.showEmptyMessage();
492             return;
493         }
494
495         this._callStackRow.hideEmptyMessage();
496         this._callStackRow.element.appendChild(this._callStackContentTreeOutline.element);
497
498         var treeElementToSelect = null;
499
500         var activeCallFrame = WebInspector.debuggerManager.activeCallFrame;
501         for (var i = 0; i < callFrames.length; ++i) {
502             var callFrameTreeElement = new WebInspector.CallFrameTreeElement(callFrames[i]);
503             if (callFrames[i] === activeCallFrame)
504                 treeElementToSelect = callFrameTreeElement;
505             this._callStackContentTreeOutline.appendChild(callFrameTreeElement);
506         }
507
508         if (treeElementToSelect)
509             treeElementToSelect.select(true, true);
510     }
511
512     _debuggerActiveCallFrameDidChange()
513     {
514         var callFrames = WebInspector.debuggerManager.callFrames;
515         if (!callFrames)
516             return;
517
518         var indexOfActiveCallFrame = callFrames.indexOf(WebInspector.debuggerManager.activeCallFrame);
519         // It is useful to turn off the step out button when there is no call frame to go through
520         // since there might be call frames in the backend that were removed when processing the call
521         // frame payload.
522         this._debuggerStepOutButtonItem.enabled = indexOfActiveCallFrame < callFrames.length - 1;
523     }
524
525     _breakpointsBeneathTreeElement(treeElement)
526     {
527         console.assert(treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement);
528         if (!(treeElement instanceof WebInspector.ResourceTreeElement) && !(treeElement instanceof WebInspector.ScriptTreeElement))
529             return [];
530
531         var breakpoints = [];
532         var breakpointTreeElements = treeElement.children;
533         for (var i = 0; i < breakpointTreeElements.length; ++i) {
534             console.assert(breakpointTreeElements[i] instanceof WebInspector.BreakpointTreeElement);
535             console.assert(breakpointTreeElements[i].breakpoint);
536             var breakpoint = breakpointTreeElements[i].breakpoint;
537             if (breakpoint)
538                 breakpoints.push(breakpoint);
539         }
540
541         return breakpoints;
542     }
543
544     _removeAllBreakpoints(breakpoints)
545     {
546         for (var i = 0; i < breakpoints.length; ++i) {
547             var breakpoint = breakpoints[i];
548             if (WebInspector.debuggerManager.isBreakpointRemovable(breakpoint))
549                 WebInspector.debuggerManager.removeBreakpoint(breakpoint);
550         }
551     }
552
553     _toggleAllBreakpoints(breakpoints, disabled)
554     {
555         for (var i = 0; i < breakpoints.length; ++i)
556             breakpoints[i].disabled = disabled;
557     }
558
559     _breakpointTreeOutlineDeleteTreeElement(treeElement)
560     {
561         console.assert(treeElement.selected);
562         console.assert(treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement);
563         if (!(treeElement instanceof WebInspector.ResourceTreeElement) && !(treeElement instanceof WebInspector.ScriptTreeElement))
564             return false;
565
566         var wasTopResourceTreeElement = treeElement.previousSibling === this._allUncaughtExceptionsBreakpointTreeElement;
567         var nextSibling = treeElement.nextSibling;
568
569         var breakpoints = this._breakpointsBeneathTreeElement(treeElement);
570         this._removeAllBreakpoints(breakpoints);
571
572         if (wasTopResourceTreeElement && nextSibling)
573             nextSibling.select(true, true);
574
575         return true;
576     }
577
578     _breakpointTreeOutlineContextMenuTreeElement(event, treeElement)
579     {
580         console.assert(treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement || treeElement.constructor === WebInspector.FolderTreeElement);
581         if (!(treeElement instanceof WebInspector.ResourceTreeElement) && !(treeElement instanceof WebInspector.ScriptTreeElement))
582             return;
583
584         let breakpoints = this._breakpointsBeneathTreeElement(treeElement);
585         let shouldDisable = breakpoints.some((breakpoint) => !breakpoint.disabled);
586
587         let removeAllResourceBreakpoints = () => {
588             this._removeAllBreakpoints(breakpoints);
589         };
590
591         let toggleAllResourceBreakpoints = () => {
592             this._toggleAllBreakpoints(breakpoints, shouldDisable);
593         };
594
595         let contextMenu = WebInspector.ContextMenu.createFromEvent(event);
596         if (shouldDisable)
597             contextMenu.appendItem(WebInspector.UIString("Disable Breakpoints"), toggleAllResourceBreakpoints);
598         else
599             contextMenu.appendItem(WebInspector.UIString("Enable Breakpoints"), toggleAllResourceBreakpoints);
600         contextMenu.appendItem(WebInspector.UIString("Delete Breakpoints"), removeAllResourceBreakpoints);
601     }
602
603     _treeSelectionDidChange(event)
604     {
605         let treeElement = event.data.selectedElement;
606         if (!treeElement)
607             return;
608
609         // Deselect any other tree elements to prevent two selections in the sidebar.
610         for (let treeOutline of this.visibleContentTreeOutlines) {
611             if (treeOutline === treeElement.treeOutline)
612                 continue;
613
614             let selectedTreeElement = treeOutline.selectedTreeElement;
615             if (selectedTreeElement)
616                 selectedTreeElement.deselect();
617         }
618
619         if (treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement) {
620             WebInspector.showSourceCode(treeElement.representedObject);
621             return;
622         }
623
624         if (treeElement instanceof WebInspector.CallFrameTreeElement) {
625             let callFrame = treeElement.callFrame;
626             WebInspector.debuggerManager.activeCallFrame = callFrame;
627             WebInspector.showSourceCodeLocation(callFrame.sourceCodeLocation);
628             return;
629         }
630
631         if (treeElement instanceof WebInspector.IssueTreeElement) {
632             WebInspector.showSourceCodeLocation(treeElement.issueMessage.sourceCodeLocation);
633             return;
634         }
635
636         if (!(treeElement instanceof WebInspector.BreakpointTreeElement) || treeElement.parent.constructor === WebInspector.FolderTreeElement)
637             return;
638
639         let breakpoint = treeElement.breakpoint;
640         if (treeElement.treeOutline === this._pauseReasonTreeOutline) {
641             WebInspector.showSourceCodeLocation(breakpoint.sourceCodeLocation);
642             return;
643         }
644
645         if (!treeElement.parent.representedObject)
646             return;
647
648         console.assert(treeElement.parent.representedObject instanceof WebInspector.SourceCode);
649         if (!(treeElement.parent.representedObject instanceof WebInspector.SourceCode))
650             return;
651
652         WebInspector.showSourceCodeLocation(breakpoint.sourceCodeLocation);
653     }
654
655     _compareTopLevelTreeElements(a, b)
656     {
657         if (a === this._globalBreakpointsFolderTreeElement)
658             return -1;
659         if (b === this._globalBreakpointsFolderTreeElement)
660             return 1;
661
662         return a.mainTitle.localeCompare(b.mainTitle);
663     }
664
665     _compareDebuggerTreeElements(a, b)
666     {
667         if (!a.debuggerObject || !b.debuggerObject)
668             return 0;
669         
670         var aLocation = a.debuggerObject.sourceCodeLocation;
671         var bLocation = b.debuggerObject.sourceCodeLocation;
672
673         var comparisonResult = aLocation.displayLineNumber - bLocation.displayLineNumber;
674         if (comparisonResult !== 0)
675             return comparisonResult;
676
677         return aLocation.displayColumnNumber - bLocation.displayColumnNumber;
678     }
679
680     _updatePauseReason()
681     {
682         this._pauseReasonTreeOutline = null;
683
684         this._updatePauseReasonGotoArrow();
685         return this._updatePauseReasonSection();
686     }
687
688     _updatePauseReasonSection()
689     {
690         var pauseData = WebInspector.debuggerManager.pauseData;
691
692         switch (WebInspector.debuggerManager.pauseReason) {
693         case WebInspector.DebuggerManager.PauseReason.Assertion:
694             // FIXME: We should include the assertion condition string.
695             console.assert(pauseData, "Expected data with an assertion, but found none.");
696             if (pauseData && pauseData.message) {
697                 this._pauseReasonTextRow.text = WebInspector.UIString("Assertion with message: %s").format(pauseData.message);
698                 return true;
699             }
700
701             this._pauseReasonTextRow.text = WebInspector.UIString("Assertion Failed");
702             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
703             return true;
704
705         case WebInspector.DebuggerManager.PauseReason.Breakpoint:
706             console.assert(pauseData, "Expected breakpoint identifier, but found none.");
707             if (pauseData && pauseData.breakpointId) {
708                 let breakpoint = WebInspector.debuggerManager.breakpointForIdentifier(pauseData.breakpointId);
709                 this._pauseReasonTreeOutline = this.createContentTreeOutline(true, true);
710                 this._pauseReasonTreeOutline.addEventListener(WebInspector.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
711
712                 let breakpointTreeElement = new WebInspector.BreakpointTreeElement(breakpoint, WebInspector.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, WebInspector.UIString("Triggered Breakpoint"));
713                 let breakpointDetailsSection = new WebInspector.DetailsSectionRow;
714                 this._pauseReasonTreeOutline.appendChild(breakpointTreeElement);
715                 breakpointDetailsSection.element.appendChild(this._pauseReasonTreeOutline.element);
716
717                 this._pauseReasonGroup.rows = [breakpointDetailsSection];
718                 return true;
719             }
720             break;
721
722         case WebInspector.DebuggerManager.PauseReason.CSPViolation:
723             console.assert(pauseData, "Expected data with a CSP Violation, but found none.");
724             if (pauseData) {
725                 // COMPATIBILITY (iOS 8): 'directive' was 'directiveText'.
726                 this._pauseReasonTextRow.text = WebInspector.UIString("Content Security Policy violation of directive: %s").format(pauseData.directive || pauseData.directiveText);
727                 this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
728                 return true;
729             }
730             break;
731
732         case WebInspector.DebuggerManager.PauseReason.DebuggerStatement:
733             this._pauseReasonTextRow.text = WebInspector.UIString("Debugger Statement");
734             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
735             return true;
736
737         case WebInspector.DebuggerManager.PauseReason.Exception:
738             console.assert(pauseData, "Expected data with an exception, but found none.");
739             if (pauseData) {
740                 // FIXME: We should improve the appearance of thrown objects. This works well for exception strings.
741                 var data = WebInspector.RemoteObject.fromPayload(pauseData);
742                 this._pauseReasonTextRow.text = WebInspector.UIString("Exception with thrown value: %s").format(data.description);
743                 this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
744                 return true;
745             }
746             break;
747
748         case WebInspector.DebuggerManager.PauseReason.PauseOnNextStatement:
749             this._pauseReasonTextRow.text = WebInspector.UIString("Immediate Pause Requested");
750             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
751             return true;
752
753         case WebInspector.DebuggerManager.PauseReason.Other:
754             console.error("Paused for unknown reason. We should always have a reason.");
755             break;
756         }
757
758         return false;
759     }
760
761     _updatePauseReasonGotoArrow()
762     {
763         this._pauseReasonLinkContainerElement.removeChildren();
764
765         var activeCallFrame = WebInspector.debuggerManager.activeCallFrame;
766         if (!activeCallFrame)
767             return;
768
769         var sourceCodeLocation = activeCallFrame.sourceCodeLocation;
770         if (!sourceCodeLocation)
771             return;
772
773         var linkElement = WebInspector.createSourceCodeLocationLink(sourceCodeLocation, false, true);
774         this._pauseReasonLinkContainerElement.appendChild(linkElement);
775     }
776
777     _addDebuggerObject(debuggerObject)
778     {
779         if (debuggerObject instanceof WebInspector.Breakpoint)
780             return this._addBreakpoint(debuggerObject);
781
782         if (debuggerObject instanceof WebInspector.IssueMessage)
783             return this._addIssue(debuggerObject);
784     }
785
786     _addIssue(issueMessage)
787     {
788         var parentTreeElement = this._addTreeElementForSourceCodeToContentTreeOutline(issueMessage.sourceCodeLocation.sourceCode);
789         if (!parentTreeElement)
790             return null;
791
792         var issueTreeElement = new WebInspector.IssueTreeElement(issueMessage);
793
794         parentTreeElement.insertChild(issueTreeElement, insertionIndexForObjectInListSortedByFunction(issueTreeElement, parentTreeElement.children, this._compareDebuggerTreeElements));
795         if (parentTreeElement.children.length === 1)
796             parentTreeElement.expand();
797
798         return issueTreeElement;
799     }
800
801     _handleIssueAdded(event)
802     {
803         var issue = event.data.issue;
804
805         // We only want to show issues originating from JavaScript source code.
806         if (!issue.sourceCodeLocation || !issue.sourceCodeLocation.sourceCode || (issue.source !== "javascript" && issue.source !== "console-api"))
807             return;
808
809         this._addIssue(issue);
810     }
811
812     _handleIssuesCleared(event)
813     {
814         var currentTreeElement = this._contentTreeOutline.children[0];
815         var issueTreeElements = [];
816
817         while (currentTreeElement && !currentTreeElement.root) {
818             if (currentTreeElement instanceof WebInspector.IssueTreeElement)
819                 issueTreeElements.push(currentTreeElement);
820             currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true);
821         }
822
823         for (var issueTreeElement of issueTreeElements)
824             issueTreeElement.parent.removeChild(issueTreeElement);
825     }
826 };
827
828 WebInspector.DebuggerSidebarPanel.OffsetSectionsStyleClassName = "offset-sections";
829 WebInspector.DebuggerSidebarPanel.DebuggerPausedStyleClassName = "paused";
830 WebInspector.DebuggerSidebarPanel.ExceptionIconStyleClassName = "breakpoint-exception-icon";
831 WebInspector.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName = "breakpoint-paused-icon";
832 WebInspector.DebuggerSidebarPanel.GlobalIconStyleClassName = "global-breakpoints-icon";
833
834 WebInspector.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey = "debugger-sidebar-panel-all-exceptions-breakpoint";
835 WebInspector.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey = "debugger-sidebar-panel-all-uncaught-exceptions-breakpoint";