ab26c2a719a72c93a3e64a5faa531d65adc45cdb
[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 WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSidebarPanel
27 {
28     constructor(contentBrowser)
29     {
30         super("debugger", WI.UIString("Debugger"), true);
31
32         this.contentBrowser = contentBrowser;
33
34         WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
35         WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceAdded, this);
36         WI.Target.addEventListener(WI.Target.Event.ResourceAdded, this._resourceAdded, this);
37
38         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
39         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this);
40         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this);
41         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this);
42         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptRemoved, this._scriptRemoved, this);
43         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptsCleared, this._scriptsCleared, this);
44         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
45         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
46         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.CallFramesDidChange, this._debuggerCallFramesDidChange, this);
47         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this._debuggerActiveCallFrameDidChange, this);
48         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.WaitingToPause, this._debuggerWaitingToPause, this);
49
50         WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingWillStart, this._timelineCapturingWillStart, this);
51         WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStopped, this._timelineCapturingStopped, this);
52
53         WI.targetManager.addEventListener(WI.TargetManager.Event.TargetAdded, this._targetAdded, this);
54         WI.targetManager.addEventListener(WI.TargetManager.Event.TargetRemoved, this._targetRemoved, this);
55
56         this._timelineRecordingWarningElement = document.createElement("div");
57         this._timelineRecordingWarningElement.classList.add("warning-banner");
58         this._timelineRecordingWarningElement.append(WI.UIString("Debugger disabled during Timeline recording"), " ");
59         let stopRecordingLink = this._timelineRecordingWarningElement.appendChild(document.createElement("a"));
60         stopRecordingLink.textContent = WI.UIString("Stop recording");
61         stopRecordingLink.addEventListener("click", () => { WI.timelineManager.stopCapturing(); });
62
63         this._breakpointsDisabledWarningElement = document.createElement("div");
64         this._breakpointsDisabledWarningElement.classList.add("warning-banner");
65         this._breakpointsDisabledWarningElement.append(WI.UIString("Breakpoints disabled"), document.createElement("br"));
66         let enableBreakpointsLink = this._breakpointsDisabledWarningElement.appendChild(document.createElement("a"));
67         enableBreakpointsLink.textContent = WI.UIString("Enable breakpoints");
68         enableBreakpointsLink.addEventListener("click", () => { WI.debuggerToggleBreakpoints(); });
69
70         this._navigationBar = new WI.NavigationBar;
71         this.addSubview(this._navigationBar);
72
73         var breakpointsImage = {src: "Images/Breakpoints.svg", width: 15, height: 15};
74         var pauseImage = {src: "Images/Pause.svg", width: 15, height: 15};
75         var resumeImage = {src: "Images/Resume.svg", width: 15, height: 15};
76         var stepOverImage = {src: "Images/StepOver.svg", width: 15, height: 15};
77         var stepIntoImage = {src: "Images/StepInto.svg", width: 15, height: 15};
78         var stepOutImage = {src: "Images/StepOut.svg", width: 15, height: 15};
79
80         var toolTip = WI.UIString("Enable all breakpoints (%s)").format(WI.toggleBreakpointsKeyboardShortcut.displayName);
81         var altToolTip = WI.UIString("Disable all breakpoints (%s)").format(WI.toggleBreakpointsKeyboardShortcut.displayName);
82
83         this._debuggerBreakpointsButtonItem = new WI.ActivateButtonNavigationItem("debugger-breakpoints", toolTip, altToolTip, breakpointsImage.src, breakpointsImage.width, breakpointsImage.height);
84         this._debuggerBreakpointsButtonItem.activated = WI.debuggerManager.breakpointsEnabled;
85         this._debuggerBreakpointsButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerToggleBreakpoints, this);
86         this._navigationBar.addNavigationItem(this._debuggerBreakpointsButtonItem);
87
88         toolTip = WI.UIString("Pause script execution (%s or %s)").format(WI.pauseOrResumeKeyboardShortcut.displayName, WI.pauseOrResumeAlternateKeyboardShortcut.displayName);
89         altToolTip = WI.UIString("Continue script execution (%s or %s)").format(WI.pauseOrResumeKeyboardShortcut.displayName, WI.pauseOrResumeAlternateKeyboardShortcut.displayName);
90
91         this._debuggerPauseResumeButtonItem = new WI.ToggleButtonNavigationItem("debugger-pause-resume", toolTip, altToolTip, pauseImage.src, resumeImage.src, pauseImage.width, pauseImage.height);
92         this._debuggerPauseResumeButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerPauseResumeToggle, this);
93         this._navigationBar.addNavigationItem(this._debuggerPauseResumeButtonItem);
94
95         this._debuggerStepOverButtonItem = new WI.ButtonNavigationItem("debugger-step-over", WI.UIString("Step over (%s or %s)").format(WI.stepOverKeyboardShortcut.displayName, WI.stepOverAlternateKeyboardShortcut.displayName), stepOverImage.src, stepOverImage.width, stepOverImage.height);
96         this._debuggerStepOverButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepOver, this);
97         this._debuggerStepOverButtonItem.enabled = false;
98         this._navigationBar.addNavigationItem(this._debuggerStepOverButtonItem);
99
100         this._debuggerStepIntoButtonItem = new WI.ButtonNavigationItem("debugger-step-into", WI.UIString("Step into (%s or %s)").format(WI.stepIntoKeyboardShortcut.displayName, WI.stepIntoAlternateKeyboardShortcut.displayName), stepIntoImage.src, stepIntoImage.width, stepIntoImage.height);
101         this._debuggerStepIntoButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepInto, this);
102         this._debuggerStepIntoButtonItem.enabled = false;
103         this._navigationBar.addNavigationItem(this._debuggerStepIntoButtonItem);
104
105         this._debuggerStepOutButtonItem = new WI.ButtonNavigationItem("debugger-step-out", WI.UIString("Step out (%s or %s)").format(WI.stepOutKeyboardShortcut.displayName, WI.stepOutAlternateKeyboardShortcut.displayName), stepOutImage.src, stepOutImage.width, stepOutImage.height);
106         this._debuggerStepOutButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepOut, this);
107         this._debuggerStepOutButtonItem.enabled = false;
108         this._navigationBar.addNavigationItem(this._debuggerStepOutButtonItem);
109
110         // Add this offset-sections class name so the sticky headers don't overlap the navigation bar.
111         this.element.classList.add(WI.DebuggerSidebarPanel.OffsetSectionsStyleClassName);
112
113         this._allExceptionsBreakpointTreeElement = new WI.BreakpointTreeElement(WI.debuggerManager.allExceptionsBreakpoint, WI.DebuggerSidebarPanel.ExceptionIconStyleClassName, WI.UIString("All Exceptions"));
114         this._allUncaughtExceptionsBreakpointTreeElement = new WI.BreakpointTreeElement(WI.debuggerManager.allUncaughtExceptionsBreakpoint, WI.DebuggerSidebarPanel.ExceptionIconStyleClassName, WI.UIString("Uncaught Exceptions"));
115         this._assertionsBreakpointTreeElement = new WI.BreakpointTreeElement(WI.debuggerManager.assertionsBreakpoint, WI.DebuggerSidebarPanel.AssertionIconStyleClassName, WI.UIString("Assertion Failures"));
116
117         this.suppressFilteringOnTreeElements([this._allExceptionsBreakpointTreeElement, this._allUncaughtExceptionsBreakpointTreeElement, this._assertionsBreakpointTreeElement]);
118
119         function showResourcesWithIssuesOnlyFilterFunction(treeElement)
120         {
121             // Issues are only shown in the scripts tree outline.
122             if (treeElement.treeOutline !== this._scriptsContentTreeOutline)
123                 return true;
124
125             // Keep issues.
126             if (treeElement instanceof WI.IssueTreeElement)
127                 return true;
128
129             // Keep resources with issues.
130             if (treeElement.hasChildren) {
131                 for (let child of treeElement.children) {
132                     if (child instanceof WI.IssueTreeElement)
133                         return true;
134                 }
135             }
136             return false;
137         }
138
139         this.filterBar.addFilterBarButton("debugger-show-resources-with-issues-only", showResourcesWithIssuesOnlyFilterFunction.bind(this), false, WI.UIString("Only show resources with issues"), WI.UIString("Show all resources"), "Images/Errors.svg", 15, 15);
140
141         this._breakpointsContentTreeOutline = this.contentTreeOutline;
142
143         let breakpointsRow = new WI.DetailsSectionRow;
144         breakpointsRow.element.appendChild(this._breakpointsContentTreeOutline.element);
145
146         let breakpointsGroup = new WI.DetailsSectionGroup([breakpointsRow]);
147         let breakpointsSection = new WI.DetailsSection("breakpoints", WI.UIString("Breakpoints"), [breakpointsGroup]);
148         this.contentView.element.appendChild(breakpointsSection.element);
149
150         this._breakpointSectionElement = breakpointsSection.element;
151
152         this._breakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
153         this._breakpointsContentTreeOutline.ondelete = this._breakpointTreeOutlineDeleteTreeElement.bind(this);
154         this._breakpointsContentTreeOutline.populateContextMenu = function(contextMenu, event, treeElement) {
155             this._breakpointTreeOutlineContextMenuTreeElement(contextMenu, event, treeElement);
156
157             WI.TreeOutline.prototype.populateContextMenu(contextMenu, event, treeElement);
158         }.bind(this);
159
160         this._breakpointsContentTreeOutline.appendChild(this._allExceptionsBreakpointTreeElement);
161         this._breakpointsContentTreeOutline.appendChild(this._allUncaughtExceptionsBreakpointTreeElement);
162
163         // COMPATIBILITY (iOS 10): DebuggerAgent.setPauseOnAssertions did not exist yet.
164         if (DebuggerAgent.setPauseOnAssertions)
165             this._breakpointsContentTreeOutline.appendChild(this._assertionsBreakpointTreeElement);
166
167         if (WI.domDebuggerManager.supported) {
168             this._domBreakpointsContentTreeOutline = this.createContentTreeOutline(true);
169             this._domBreakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.ElementAdded, this._domBreakpointAddedOrRemoved, this);
170             this._domBreakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.ElementRemoved, this._domBreakpointAddedOrRemoved, this);
171             this._domBreakpointTreeController = new WI.DOMBreakpointTreeController(this._domBreakpointsContentTreeOutline);
172
173             this._domBreakpointsRow = new WI.DetailsSectionRow(WI.UIString("No Breakpoints"));
174             this._domBreakpointsRow.element.appendChild(this._domBreakpointsContentTreeOutline.element);
175             this._domBreakpointsRow.showEmptyMessage();
176
177             const defaultCollapsed = true;
178
179             let domBreakpointsGroup = new WI.DetailsSectionGroup([this._domBreakpointsRow]);
180             this._domBreakpointsSection = new WI.DetailsSection("dom-breakpoints", WI.UIString("DOM Breakpoints"), [domBreakpointsGroup], null, defaultCollapsed);
181             this.contentView.element.appendChild(this._domBreakpointsSection.element);
182
183             this._xhrBreakpointsContentTreeOutline = this.createContentTreeOutline(true);
184             this._xhrBreakpointTreeController = new WI.XHRBreakpointTreeController(this._xhrBreakpointsContentTreeOutline);
185
186             this._xhrBreakpointsRow = new WI.DetailsSectionRow;
187             this._xhrBreakpointsRow.element.appendChild(this._xhrBreakpointsContentTreeOutline.element);
188
189             let navigationBar = new WI.NavigationBar;
190             let navigationBarWrapper = document.createElement("div");
191             navigationBarWrapper.appendChild(navigationBar.element);
192
193             let addXHRBreakpointButton = new WI.ButtonNavigationItem("add-xhr-breakpoint", WI.UIString("Add XHR Breakpoint"), "Images/Plus13.svg", 13, 13);
194             addXHRBreakpointButton.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._addXHRBreakpointButtonClicked, this);
195             navigationBar.addNavigationItem(addXHRBreakpointButton);
196
197             let xhrBreakpointsGroup = new WI.DetailsSectionGroup([this._xhrBreakpointsRow]);
198             let xhrBreakpointsSection = new WI.DetailsSection("xhr-breakpoints", WI.UIString("XHR Breakpoints"), [xhrBreakpointsGroup], navigationBarWrapper, defaultCollapsed);
199             this.contentView.element.appendChild(xhrBreakpointsSection.element);
200         }
201
202         this._scriptsContentTreeOutline = this.createContentTreeOutline();
203         this._scriptsContentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
204         this._scriptsContentTreeOutline.includeSourceMapResourceChildren = true;
205
206         let scriptsRow = new WI.DetailsSectionRow;
207         scriptsRow.element.appendChild(this._scriptsContentTreeOutline.element);
208
209         let scriptsGroup = new WI.DetailsSectionGroup([scriptsRow]);
210         this._scriptsSection = new WI.DetailsSection("scripts", WI.UIString("Sources"), [scriptsGroup]);
211         this.contentView.element.appendChild(this._scriptsSection.element);
212
213         const suppressFiltering = true;
214         this._callStackTreeOutline = this.createContentTreeOutline(suppressFiltering);
215         this._callStackTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
216
217         this._mainTargetTreeElement = new WI.ThreadTreeElement(WI.mainTarget);
218         this._callStackTreeOutline.appendChild(this._mainTargetTreeElement);
219
220         this._updateCallStackTreeOutline();
221
222         this._callStackRow = new WI.DetailsSectionRow;
223         this._callStackRow.element.appendChild(this._callStackTreeOutline.element);
224
225         this._callStackGroup = new WI.DetailsSectionGroup([this._callStackRow]);
226         this._callStackSection = new WI.DetailsSection("call-stack", WI.UIString("Call Stack"), [this._callStackGroup]);
227
228         this._showingSingleThreadCallStack = true;
229
230         this._activeCallFrameTreeElement = null;
231
232         this._pauseReasonTreeOutline = null;
233
234         this._pauseReasonLinkContainerElement = document.createElement("span");
235         this._pauseReasonTextRow = new WI.DetailsSectionTextRow;
236         this._pauseReasonGroup = new WI.DetailsSectionGroup([this._pauseReasonTextRow]);
237         this._pauseReasonSection = new WI.DetailsSection("paused-reason", WI.UIString("Pause Reason"), [this._pauseReasonGroup], this._pauseReasonLinkContainerElement);
238
239         WI.Breakpoint.addEventListener(WI.Breakpoint.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
240         WI.IssueMessage.addEventListener(WI.IssueMessage.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
241         WI.issueManager.addEventListener(WI.IssueManager.Event.IssueWasAdded, this._handleIssueAdded, this);
242         WI.issueManager.addEventListener(WI.IssueManager.Event.Cleared, this._handleIssuesCleared, this);
243
244         if (WI.frameResourceManager.mainFrame)
245             this._addResourcesRecursivelyForFrame(WI.frameResourceManager.mainFrame);
246
247         for (var script of WI.debuggerManager.knownNonResourceScripts)
248             this._addScript(script);
249
250         if (WI.debuggerManager.paused)
251             this._debuggerDidPause(null);
252
253         if (WI.timelineManager.isCapturing() && WI.debuggerManager.breakpointsDisabledTemporarily)
254             this._timelineCapturingWillStart(null);
255
256         this._updateBreakpointsDisabledBanner();
257     }
258
259     // Public
260
261     get minimumWidth()
262     {
263         return this._navigationBar.minimumWidth;
264     }
265
266     closed()
267     {
268         super.closed();
269
270         this._domBreakpointTreeController.disconnect();
271         this._domBreakpointTreeController = null;
272
273         WI.Frame.removeEventListener(null, null, this);
274         WI.debuggerManager.removeEventListener(null, null, this);
275         WI.Breakpoint.removeEventListener(null, null, this);
276         WI.IssueMessage.removeEventListener(null, null, this);
277     }
278
279     showDefaultContentView()
280     {
281         if (WI.frameResourceManager.mainFrame) {
282             let mainTreeElement = this._scriptsContentTreeOutline.findTreeElement(WI.frameResourceManager.mainFrame.mainResource);
283             if (mainTreeElement && this.showDefaultContentViewForTreeElement(mainTreeElement))
284                 return;
285         }
286
287         let currentTreeElement = this._scriptsContentTreeOutline.children[0];
288         while (currentTreeElement && !currentTreeElement.root) {
289             if (currentTreeElement instanceof WI.ResourceTreeElement || currentTreeElement instanceof WI.ScriptTreeElement) {
290                 if (this.showDefaultContentViewForTreeElement(currentTreeElement))
291                     return;
292             }
293
294             currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true);
295         }
296     }
297
298     treeElementForRepresentedObject(representedObject)
299     {
300         // The main resource is used as the representedObject instead of Frame in our tree.
301         if (representedObject instanceof WI.Frame)
302             representedObject = representedObject.mainResource;
303
304         let treeElement = this._breakpointsContentTreeOutline.findTreeElement(representedObject);
305         if (!treeElement)
306             treeElement = this._scriptsContentTreeOutline.findTreeElement(representedObject);
307
308         if (treeElement)
309             return treeElement;
310
311         // Only special case Script objects.
312         if (!(representedObject instanceof WI.Script)) {
313             console.error("Didn't find a TreeElement for representedObject", representedObject);
314             return null;
315         }
316
317         // If the Script has a URL we should have found it earlier.
318         if (representedObject.url) {
319             console.error("Didn't find a ScriptTreeElement for a Script with a URL.");
320             return null;
321         }
322
323         // Since the Script does not have a URL we consider it an 'anonymous' script. These scripts happen from calls to
324         // window.eval() or browser features like Auto Fill and Reader. They are not normally added to the sidebar, but since
325         // we have a ScriptContentView asking for the tree element we will make a ScriptTreeElement on demand and add it.
326
327         return this._addScript(representedObject);
328     }
329
330     // Protected
331
332     saveStateToCookie(cookie)
333     {
334         console.assert(cookie);
335
336         var selectedTreeElement = this._breakpointsContentTreeOutline.selectedTreeElement;
337         if (!selectedTreeElement) {
338             super.saveStateToCookie(cookie);
339             return;
340         }
341
342         var representedObject = selectedTreeElement.representedObject;
343
344         if (representedObject === WI.debuggerManager.allExceptionsBreakpoint) {
345             cookie[WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey] = true;
346             return;
347         }
348
349         if (representedObject === WI.debuggerManager.allUncaughtExceptionsBreakpoint) {
350             cookie[WI.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey] = true;
351             return;
352         }
353
354         if (representedObject === WI.debuggerManager.assertionsBreakpoint) {
355             cookie[WI.DebuggerSidebarPanel.SelectedAssertionsCookieKey] = true;
356             return;
357         }
358
359         if (representedObject === WI.domDebuggerManager.allRequestsBreakpoint) {
360             cookie[WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey] = true;
361             return;
362         }
363
364         super.saveStateToCookie(cookie);
365     }
366
367     restoreStateFromCookie(cookie, relaxedMatchDelay)
368     {
369         console.assert(cookie);
370
371         // Eagerly resolve the special breakpoints; otherwise, use the default behavior.
372         if (cookie[WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey])
373             this._allExceptionsBreakpointTreeElement.revealAndSelect();
374         else if (cookie[WI.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey])
375             this._allUncaughtExceptionsBreakpointTreeElement.revealAndSelect();
376         else if (cookie[WI.DebuggerSidebarPanel.SelectedAssertionsCookieKey])
377             this._assertionsBreakpointTreeElement.revealAndSelect();
378         else if (cookie[WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey])
379             this._xhrBreakpointTreeController.revealAndSelect(WI.domDebuggerManager.allRequestsBreakpoint);
380         else
381             super.restoreStateFromCookie(cookie, relaxedMatchDelay);
382     }
383
384     // Private
385
386     _debuggerWaitingToPause(event)
387     {
388         this._debuggerPauseResumeButtonItem.enabled = false;
389     }
390
391     _debuggerDidPause(event)
392     {
393         this.contentView.element.insertBefore(this._callStackSection.element, this.contentView.element.firstChild);
394
395         if (this._updatePauseReason())
396             this.contentView.element.insertBefore(this._pauseReasonSection.element, this.contentView.element.firstChild);
397
398         this._debuggerPauseResumeButtonItem.enabled = true;
399         this._debuggerPauseResumeButtonItem.toggled = true;
400         this._debuggerStepOverButtonItem.enabled = true;
401         this._debuggerStepIntoButtonItem.enabled = true;
402         this._debuggerStepOutButtonItem.enabled = true;
403
404         this.element.classList.add(WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName);
405     }
406
407     _debuggerDidResume(event)
408     {
409         this._callStackSection.element.remove();
410
411         this._pauseReasonSection.element.remove();
412
413         this._debuggerPauseResumeButtonItem.enabled = true;
414         this._debuggerPauseResumeButtonItem.toggled = false;
415         this._debuggerStepOverButtonItem.enabled = false;
416         this._debuggerStepIntoButtonItem.enabled = false;
417         this._debuggerStepOutButtonItem.enabled = false;
418
419         this.element.classList.remove(WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName);
420     }
421
422     _breakpointsEnabledDidChange(event)
423     {
424         this._debuggerBreakpointsButtonItem.activated = WI.debuggerManager.breakpointsEnabled;
425
426         this._updateBreakpointsDisabledBanner();
427     }
428
429     _addBreakpoint(breakpoint)
430     {
431         let sourceCode = breakpoint.sourceCodeLocation.displaySourceCode;
432         if (!sourceCode)
433             return null;
434
435         if (this._breakpointsContentTreeOutline.findTreeElement(breakpoint))
436             return;
437
438         let parentTreeElement = this._addTreeElementForSourceCodeToTreeOutline(sourceCode, this._breakpointsContentTreeOutline);
439
440         // Mark disabled breakpoints as resolved if there is source code loaded with that URL.
441         // This gives the illusion the breakpoint was resolved, but since we don't send disabled
442         // breakpoints to the backend we don't know for sure. If the user enables the breakpoint
443         // it will be resolved properly.
444         if (breakpoint.disabled)
445             breakpoint.resolved = true;
446
447         let breakpointTreeElement = new WI.BreakpointTreeElement(breakpoint);
448         parentTreeElement.insertChild(breakpointTreeElement, insertionIndexForObjectInListSortedByFunction(breakpointTreeElement, parentTreeElement.children, this._compareTreeElements));
449         if (parentTreeElement.children.length === 1)
450             parentTreeElement.expand();
451         return breakpointTreeElement;
452     }
453
454     _addBreakpointsForSourceCode(sourceCode)
455     {
456         var breakpoints = WI.debuggerManager.breakpointsForSourceCode(sourceCode);
457         for (var i = 0; i < breakpoints.length; ++i)
458             this._addBreakpoint(breakpoints[i], sourceCode);
459     }
460
461     _addIssuesForSourceCode(sourceCode)
462     {
463         var issues = WI.issueManager.issuesForSourceCode(sourceCode);
464         for (var issue of issues)
465             this._addIssue(issue);
466     }
467
468     _addTreeElementForSourceCodeToTreeOutline(sourceCode, treeOutline)
469     {
470         let treeElement = treeOutline.getCachedTreeElement(sourceCode);
471         if (!treeElement) {
472             if (sourceCode instanceof WI.SourceMapResource)
473                 treeElement = new WI.SourceMapResourceTreeElement(sourceCode);
474             else if (sourceCode instanceof WI.Resource)
475                 treeElement = new WI.ResourceTreeElement(sourceCode);
476             else if (sourceCode instanceof WI.Script)
477                 treeElement = new WI.ScriptTreeElement(sourceCode);
478         }
479
480         if (!treeElement) {
481             console.error("Unknown sourceCode instance", sourceCode);
482             return null;
483         }
484
485         if (!treeElement.parent) {
486             treeElement.hasChildren = false;
487             treeElement.expand();
488
489             treeOutline.insertChild(treeElement, insertionIndexForObjectInListSortedByFunction(treeElement, treeOutline.children, this._compareTopLevelTreeElements.bind(this)));
490         }
491
492         return treeElement;
493     }
494
495     _addResourcesRecursivelyForFrame(frame)
496     {
497         this._addResource(frame.mainResource);
498
499         for (let resource of frame.resourceCollection.items)
500             this._addResource(resource);
501
502         for (let childFrame of frame.childFrameCollection.items)
503             this._addResourcesRecursivelyForFrame(childFrame);
504     }
505
506     _resourceAdded(event)
507     {
508         this._addResource(event.data.resource);
509     }
510
511     _addResource(resource)
512     {
513         if (![WI.Resource.Type.Document, WI.Resource.Type.Script].includes(resource.type))
514             return;
515
516         let treeElement = this._addTreeElementForSourceCodeToTreeOutline(resource, this._scriptsContentTreeOutline);
517         this._addBreakpointsForSourceCode(resource);
518         this._addIssuesForSourceCode(resource);
519
520         if (this.parentSidebar && !this.contentBrowser.currentContentView)
521             this.showDefaultContentViewForTreeElement(treeElement);
522     }
523
524     _mainResourceDidChange(event)
525     {
526         if (event.target.isMainFrame()) {
527             // Aggressively prune resources now so the old resources are removed before
528             // the new main resource is added below. This avoids a visual flash when the
529             // prune normally happens on a later event loop cycle.
530             this.pruneStaleResourceTreeElements();
531             this.contentBrowser.contentViewContainer.closeAllContentViews();
532         }
533
534         if (!event.data.oldMainResource) {
535             var resource = event.target.mainResource;
536             this._addTreeElementForSourceCodeToTreeOutline(resource, this._scriptsContentTreeOutline);
537             this._addBreakpointsForSourceCode(resource);
538             this._addIssuesForSourceCode(resource);
539         }
540     }
541
542     _timelineCapturingWillStart(event)
543     {
544         this._debuggerBreakpointsButtonItem.enabled = false;
545         this._debuggerPauseResumeButtonItem.enabled = false;
546
547         this.contentView.element.insertBefore(this._timelineRecordingWarningElement, this.contentView.element.firstChild);
548         this._updateBreakpointsDisabledBanner();
549     }
550
551     _timelineCapturingStopped(event)
552     {
553         this._debuggerBreakpointsButtonItem.enabled = true;
554         this._debuggerPauseResumeButtonItem.enabled = true;
555
556         this._timelineRecordingWarningElement.remove();
557         this._updateBreakpointsDisabledBanner();
558     }
559
560     _updateBreakpointsDisabledBanner()
561     {
562         let breakpointsEnabled = WI.debuggerManager.breakpointsEnabled;
563         let timelineWarningShowing = !!this._timelineRecordingWarningElement.parentElement;
564
565         if (!breakpointsEnabled && !timelineWarningShowing)
566             this.contentView.element.insertBefore(this._breakpointsDisabledWarningElement, this.contentView.element.firstChild);
567         else
568             this._breakpointsDisabledWarningElement.remove();
569     }
570
571     _scriptAdded(event)
572     {
573         this._addScript(event.data.script);
574     }
575
576     _addScript(script)
577     {
578         // COMPATIBILITY(iOS 9): Backends could send the frontend built-in code, filter out JSC internals.
579         if (!script.url && !script.sourceURL)
580             return null;
581
582         // In general, do not show dynamically added script elements.
583         if (script.dynamicallyAddedScriptElement && !script.sourceURL)
584             return null;
585
586         // Don't add breakpoints if the script is represented by a Resource. They were
587         // already added by _resourceAdded.
588         if (script.resource)
589             return null;
590
591         let treeElement = this._addTreeElementForSourceCodeToTreeOutline(script, this._scriptsContentTreeOutline);
592         this._addBreakpointsForSourceCode(script);
593         this._addIssuesForSourceCode(script);
594
595         if (this.parentSidebar && !this.contentBrowser.currentContentView)
596             this.showDefaultContentViewForTreeElement(treeElement);
597
598         return treeElement;
599     }
600
601     _scriptRemoved(event)
602     {
603         function removeScript(script, treeOutline)
604         {
605             let scriptTreeElement = treeOutline.getCachedTreeElement(script);
606             if (scriptTreeElement)
607                 scriptTreeElement.parent.removeChild(scriptTreeElement);
608         }
609
610         let script = event.data.script;
611         removeScript(script, this._breakpointsContentTreeOutline);
612         removeScript(script, this._scriptsContentTreeOutline);
613     }
614
615     _scriptsCleared(event)
616     {
617         for (var i = this._breakpointsContentTreeOutline.children.length - 1; i >= 0; --i) {
618             var treeElement = this._breakpointsContentTreeOutline.children[i];
619             if (!(treeElement instanceof WI.ScriptTreeElement))
620                 continue;
621
622             this._breakpointsContentTreeOutline.removeChildAtIndex(i, true, true);
623         }
624
625         this._scriptsContentTreeOutline.removeChildren();
626
627         this._addResourcesRecursivelyForFrame(WI.frameResourceManager.mainFrame);
628     }
629
630     _breakpointAdded(event)
631     {
632         var breakpoint = event.data.breakpoint;
633         this._addBreakpoint(breakpoint);
634     }
635
636     _breakpointRemoved(event)
637     {
638         var breakpoint = event.data.breakpoint;
639
640         if (this._pauseReasonTreeOutline) {
641             var pauseReasonBreakpointTreeElement = this._pauseReasonTreeOutline.getCachedTreeElement(breakpoint);
642             if (pauseReasonBreakpointTreeElement)
643                 pauseReasonBreakpointTreeElement.removeStatusImage();
644         }
645
646         var breakpointTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(breakpoint);
647         console.assert(breakpointTreeElement);
648         if (!breakpointTreeElement)
649             return;
650
651         this._removeDebuggerTreeElement(breakpointTreeElement);
652     }
653
654     _findThreadTreeElementForTarget(target)
655     {
656         for (let child of this._callStackTreeOutline.children) {
657             if (child.target === target)
658                 return child;
659         }
660
661         return null;
662     }
663
664     _targetAdded(event)
665     {
666         let target = event.data.target;
667         let treeElement = new WI.ThreadTreeElement(target);
668         this._callStackTreeOutline.appendChild(treeElement);
669
670         this._updateCallStackTreeOutline();
671     }
672
673     _targetRemoved(event)
674     {
675         let target = event.data.target;
676         let treeElement = this._findThreadTreeElementForTarget(target);
677         this._callStackTreeOutline.removeChild(treeElement);
678
679         this._updateCallStackTreeOutline();
680     }
681
682     _updateCallStackTreeOutline()
683     {
684         let singleThreadShowing = WI.targets.size === 1;
685         this._callStackTreeOutline.element.classList.toggle("single-thread", singleThreadShowing);
686         this._mainTargetTreeElement.selectable = !singleThreadShowing;
687     }
688
689     _handleDebuggerObjectDisplayLocationDidChange(event)
690     {
691         var debuggerObject = event.target;
692
693         if (event.data.oldDisplaySourceCode === debuggerObject.sourceCodeLocation.displaySourceCode)
694             return;
695
696         var debuggerTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(debuggerObject);
697         if (!debuggerTreeElement)
698             return;
699
700         // A known debugger object (breakpoint, issueMessage, etc.) moved between resources, remove the old tree element
701         // and create a new tree element with the updated file.
702
703         var wasSelected = debuggerTreeElement.selected;
704
705         this._removeDebuggerTreeElement(debuggerTreeElement);
706         var newDebuggerTreeElement = this._addDebuggerObject(debuggerObject);
707
708         if (newDebuggerTreeElement && wasSelected)
709             newDebuggerTreeElement.revealAndSelect(true, false, true, true);
710     }
711
712     _removeDebuggerTreeElement(debuggerTreeElement)
713     {
714         // If this is a BreakpointTreeElement being deleted because of a cause
715         // outside of the TreeOutline then deselect if it is selected to avoid
716         // TreeOutline selection changes causing unexpected ContentView changes.
717         if (!debuggerTreeElement.__deletedViaDeleteKeyboardShortcut)
718             debuggerTreeElement.deselect();
719
720         let parentTreeElement = debuggerTreeElement.parent;
721         parentTreeElement.removeChild(debuggerTreeElement);
722
723         console.assert(parentTreeElement.parent === this._breakpointsContentTreeOutline);
724         if (parentTreeElement.children.length)
725             return;
726
727         parentTreeElement.treeOutline.removeChild(parentTreeElement);
728     }
729
730     _debuggerCallFramesDidChange(event)
731     {
732         let target = event.data.target;
733         let treeElement = this._findThreadTreeElementForTarget(target);
734         treeElement.refresh();
735     }
736
737     _debuggerActiveCallFrameDidChange()
738     {
739         if (this._activeCallFrameTreeElement) {
740             this._activeCallFrameTreeElement.isActiveCallFrame = false;
741             this._activeCallFrameTreeElement = null;
742         }
743
744         if (!WI.debuggerManager.activeCallFrame)
745             return;
746
747         this._activeCallFrameTreeElement = this._callStackTreeOutline.findTreeElement(WI.debuggerManager.activeCallFrame);
748         if (this._activeCallFrameTreeElement)
749             this._activeCallFrameTreeElement.isActiveCallFrame = true;
750     }
751
752     _breakpointsBeneathTreeElement(treeElement)
753     {
754         console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement);
755         if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
756             return [];
757
758         var breakpoints = [];
759         var breakpointTreeElements = treeElement.children;
760         for (var i = 0; i < breakpointTreeElements.length; ++i) {
761             console.assert(breakpointTreeElements[i] instanceof WI.BreakpointTreeElement);
762             console.assert(breakpointTreeElements[i].breakpoint);
763             var breakpoint = breakpointTreeElements[i].breakpoint;
764             if (breakpoint)
765                 breakpoints.push(breakpoint);
766         }
767
768         return breakpoints;
769     }
770
771     _removeAllBreakpoints(breakpoints)
772     {
773         for (var i = 0; i < breakpoints.length; ++i) {
774             var breakpoint = breakpoints[i];
775             if (WI.debuggerManager.isBreakpointRemovable(breakpoint))
776                 WI.debuggerManager.removeBreakpoint(breakpoint);
777         }
778     }
779
780     _toggleAllBreakpoints(breakpoints, disabled)
781     {
782         for (var i = 0; i < breakpoints.length; ++i)
783             breakpoints[i].disabled = disabled;
784     }
785
786     _breakpointTreeOutlineDeleteTreeElement(treeElement)
787     {
788         console.assert(treeElement.selected);
789         console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement);
790         if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
791             return false;
792
793         var wasTopResourceTreeElement = treeElement.previousSibling === this._assertionsBreakpointTreeElement || treeElement.previousSibling === this._allUncaughtExceptionsBreakpointTreeElement;
794         var nextSibling = treeElement.nextSibling;
795
796         var breakpoints = this._breakpointsBeneathTreeElement(treeElement);
797         this._removeAllBreakpoints(breakpoints);
798
799         if (wasTopResourceTreeElement && nextSibling)
800             nextSibling.select(true, true);
801
802         return true;
803     }
804
805     _breakpointTreeOutlineContextMenuTreeElement(contextMenu, event, treeElement)
806     {
807         // This check is necessary since the context menu is created by the TreeOutline, meaning
808         // that any child could be the target of the context menu event.
809         if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
810             return;
811
812         let breakpoints = this._breakpointsBeneathTreeElement(treeElement);
813         let shouldDisable = breakpoints.some((breakpoint) => !breakpoint.disabled);
814
815         let removeAllResourceBreakpoints = () => {
816             this._removeAllBreakpoints(breakpoints);
817         };
818
819         let toggleAllResourceBreakpoints = () => {
820             this._toggleAllBreakpoints(breakpoints, shouldDisable);
821         };
822
823         if (shouldDisable)
824             contextMenu.appendItem(WI.UIString("Disable Breakpoints"), toggleAllResourceBreakpoints);
825         else
826             contextMenu.appendItem(WI.UIString("Enable Breakpoints"), toggleAllResourceBreakpoints);
827         contextMenu.appendItem(WI.UIString("Delete Breakpoints"), removeAllResourceBreakpoints);
828     }
829
830     _treeSelectionDidChange(event)
831     {
832         if (!this.visible)
833             return;
834
835         let treeElement = event.data.selectedElement;
836         if (!treeElement)
837             return;
838
839         const options = {
840             ignoreNetworkTab: true,
841             ignoreSearchTab: true,
842         };
843
844         if (treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement) {
845             WI.showSourceCode(treeElement.representedObject, options);
846             return;
847         }
848
849         if (treeElement instanceof WI.CallFrameTreeElement) {
850             let callFrame = treeElement.callFrame;
851             if (callFrame.id)
852                 WI.debuggerManager.activeCallFrame = callFrame;
853
854             if (callFrame.sourceCodeLocation)
855                 WI.showSourceCodeLocation(callFrame.sourceCodeLocation, options);
856
857             return;
858         }
859
860         if (treeElement instanceof WI.IssueTreeElement) {
861             WI.showSourceCodeLocation(treeElement.issueMessage.sourceCodeLocation, options);
862             return;
863         }
864
865         if (!(treeElement instanceof WI.BreakpointTreeElement))
866             return;
867
868         let breakpoint = treeElement.breakpoint;
869         if (treeElement.treeOutline === this._pauseReasonTreeOutline) {
870             WI.showSourceCodeLocation(breakpoint.sourceCodeLocation, options);
871             return;
872         }
873
874         if (!treeElement.parent.representedObject)
875             return;
876
877         console.assert(treeElement.parent.representedObject instanceof WI.SourceCode);
878         if (!(treeElement.parent.representedObject instanceof WI.SourceCode))
879             return;
880
881         WI.showSourceCodeLocation(breakpoint.sourceCodeLocation, options);
882     }
883
884     _compareTopLevelTreeElements(a, b)
885     {
886         function isSpecialBreakpoint(treeElement)
887         {
888             return treeElement.representedObject === WI.debuggerManager.allExceptionsBreakpoint
889                 || treeElement.representedObject === WI.debuggerManager.allUncaughtExceptionsBreakpoint
890                 || treeElement.representedObject === WI.debuggerManager.assertionsBreakpoint;
891         }
892
893         if (isSpecialBreakpoint(a))
894             return -1;
895         if (isSpecialBreakpoint(b))
896             return 1;
897
898         return a.mainTitle.extendedLocaleCompare(b.mainTitle);
899     }
900
901     _compareTreeElements(a, b)
902     {
903         if (!a.representedObject || !b.representedObject)
904             return 0;
905
906         let aLocation = a.representedObject.sourceCodeLocation;
907         let bLocation = b.representedObject.sourceCodeLocation;
908         if (!aLocation || !bLocation)
909             return 0;
910
911         var comparisonResult = aLocation.displayLineNumber - bLocation.displayLineNumber;
912         if (comparisonResult !== 0)
913             return comparisonResult;
914
915         return aLocation.displayColumnNumber - bLocation.displayColumnNumber;
916     }
917
918     _updatePauseReason()
919     {
920         this._pauseReasonTreeOutline = null;
921
922         this._updatePauseReasonGotoArrow();
923         return this._updatePauseReasonSection();
924     }
925
926     _updatePauseReasonSection()
927     {
928         let target = WI.debuggerManager.activeCallFrame.target;
929         let targetData = WI.debuggerManager.dataForTarget(target);
930         let {pauseReason, pauseData} = targetData;
931
932         switch (pauseReason) {
933         case WI.DebuggerManager.PauseReason.Assertion:
934             // FIXME: We should include the assertion condition string.
935             console.assert(pauseData, "Expected data with an assertion, but found none.");
936             if (pauseData && pauseData.message) {
937                 this._pauseReasonTextRow.text = WI.UIString("Assertion with message: %s").format(pauseData.message);
938                 return true;
939             }
940
941             this._pauseReasonTextRow.text = WI.UIString("Assertion Failed");
942             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
943             return true;
944
945         case WI.DebuggerManager.PauseReason.Breakpoint:
946             console.assert(pauseData, "Expected breakpoint identifier, but found none.");
947             if (pauseData && pauseData.breakpointId) {
948                 const suppressFiltering = true;
949                 this._pauseReasonTreeOutline = this.createContentTreeOutline(suppressFiltering);
950                 this._pauseReasonTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
951
952                 let breakpoint = WI.debuggerManager.breakpointForIdentifier(pauseData.breakpointId);
953                 let breakpointTreeElement = new WI.BreakpointTreeElement(breakpoint, WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, WI.UIString("Triggered Breakpoint"));
954                 let breakpointDetailsSection = new WI.DetailsSectionRow;
955                 this._pauseReasonTreeOutline.appendChild(breakpointTreeElement);
956                 breakpointDetailsSection.element.appendChild(this._pauseReasonTreeOutline.element);
957
958                 this._pauseReasonGroup.rows = [breakpointDetailsSection];
959                 return true;
960             }
961             break;
962
963         case WI.DebuggerManager.PauseReason.CSPViolation:
964             console.assert(pauseData, "Expected data with a CSP Violation, but found none.");
965             if (pauseData) {
966                 // COMPATIBILITY (iOS 8): 'directive' was 'directiveText'.
967                 this._pauseReasonTextRow.text = WI.UIString("Content Security Policy violation of directive: %s").format(pauseData.directive || pauseData.directiveText);
968                 this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
969                 return true;
970             }
971             break;
972
973         case WI.DebuggerManager.PauseReason.DebuggerStatement:
974             this._pauseReasonTextRow.text = WI.UIString("Debugger Statement");
975             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
976             return true;
977
978         case WI.DebuggerManager.PauseReason.DOM:
979             console.assert(WI.domDebuggerManager.supported);
980             console.assert(pauseData, "Expected DOM breakpoint data, but found none.");
981             if (pauseData && pauseData.nodeId) {
982                 let domNode = WI.domTreeManager.nodeForId(pauseData.nodeId);
983                 let domBreakpoints = WI.domDebuggerManager.domBreakpointsForNode(domNode);
984                 let domBreakpoint;
985                 for (let breakpoint of domBreakpoints) {
986                     if (breakpoint.type === pauseData.type) {
987                         domBreakpoint = breakpoint;
988                         break;
989                     }
990                 }
991
992                 if (!domBreakpoint)
993                     return;
994
995                 const suppressFiltering = true;
996                 this._pauseReasonTreeOutline = this.createContentTreeOutline(suppressFiltering);
997
998                 let type = WI.DOMBreakpointTreeElement.displayNameForType(domBreakpoint.type);
999                 let domBreakpointTreeElement = new WI.DOMBreakpointTreeElement(domBreakpoint, WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, type);
1000                 let domBreakpointRow = new WI.DetailsSectionRow;
1001                 this._pauseReasonTreeOutline.appendChild(domBreakpointTreeElement);
1002                 domBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1003
1004                 let ownerElementRow = new WI.DetailsSectionSimpleRow(WI.UIString("Element"), WI.linkifyNodeReference(domNode));
1005                 this._pauseReasonGroup.rows = [domBreakpointRow, ownerElementRow];
1006
1007                 if (domBreakpoint.type !== WI.DOMBreakpoint.Type.SubtreeModified)
1008                     return true;
1009
1010                 console.assert(pauseData.targetNode);
1011
1012                 let remoteObject = WI.RemoteObject.fromPayload(pauseData.targetNode, target);
1013                 remoteObject.pushNodeToFrontend((nodeId) => {
1014                     if (!nodeId)
1015                         return;
1016
1017                     let node = WI.domTreeManager.nodeForId(nodeId);
1018                     console.assert(node, "Missing node for id.", nodeId);
1019                     if (!node)
1020                         return;
1021
1022                     let fragment = document.createDocumentFragment();
1023                     let description = pauseData.insertion ? WI.UIString("Child added to ") : WI.UIString("Removed descendant ");
1024                     fragment.append(description, WI.linkifyNodeReference(node));
1025
1026                     let targetDescriptionRow = new WI.DetailsSectionSimpleRow(WI.UIString("Details"), fragment);
1027                     targetDescriptionRow.element.classList.add("target-description");
1028
1029                     this._pauseReasonGroup.rows = this._pauseReasonGroup.rows.concat(targetDescriptionRow);
1030                 });
1031
1032                 return true;
1033             }
1034             break;
1035
1036         case WI.DebuggerManager.PauseReason.Exception:
1037             console.assert(pauseData, "Expected data with an exception, but found none.");
1038             if (pauseData) {
1039                 // FIXME: We should improve the appearance of thrown objects. This works well for exception strings.
1040                 var data = WI.RemoteObject.fromPayload(pauseData, target);
1041                 this._pauseReasonTextRow.text = WI.UIString("Exception with thrown value: %s").format(data.description);
1042                 this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1043                 return true;
1044             }
1045             break;
1046
1047         case WI.DebuggerManager.PauseReason.PauseOnNextStatement:
1048             this._pauseReasonTextRow.text = WI.UIString("Immediate Pause Requested");
1049             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1050             return true;
1051
1052         case WI.DebuggerManager.PauseReason.XHR:
1053             console.assert(WI.domDebuggerManager.supported);
1054             console.assert(pauseData, "Expected XHR breakpoint data, but found none.");
1055             if (pauseData) {
1056                 if (pauseData.breakpointURL) {
1057                     let xhrBreakpoint = WI.domDebuggerManager.xhrBreakpointForURL(pauseData.breakpointURL);
1058                     console.assert(xhrBreakpoint, "Expected XHR breakpoint for URL.", pauseData.breakpointURL);
1059
1060                     this._pauseReasonTreeOutline = this.createContentTreeOutline(true);
1061
1062                     let xhrBreakpointTreeElement = new WI.XHRBreakpointTreeElement(xhrBreakpoint, WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, WI.UIString("Triggered XHR Breakpoint"));
1063                     let xhrBreakpointRow = new WI.DetailsSectionRow;
1064                     this._pauseReasonTreeOutline.appendChild(xhrBreakpointTreeElement);
1065                     xhrBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1066
1067                     this._pauseReasonTextRow.text = pauseData.url;
1068                     this._pauseReasonGroup.rows = [xhrBreakpointRow, this._pauseReasonTextRow];
1069                 } else {
1070                     console.assert(pauseData.breakpointURL === "", "Should be the All Requests breakpoint which has an empty URL");
1071                     this._pauseReasonTextRow.text = WI.UIString("Requesting: %s").format(pauseData.url);
1072                     this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1073                 }
1074                 this._pauseReasonTextRow.element.title = pauseData.url;
1075                 return true;
1076             }
1077             break;
1078
1079         case WI.DebuggerManager.PauseReason.Other:
1080             console.error("Paused for unknown reason. We should always have a reason.");
1081             break;
1082         }
1083
1084         return false;
1085     }
1086
1087     _updatePauseReasonGotoArrow()
1088     {
1089         this._pauseReasonLinkContainerElement.removeChildren();
1090
1091         var activeCallFrame = WI.debuggerManager.activeCallFrame;
1092         if (!activeCallFrame)
1093             return;
1094
1095         var sourceCodeLocation = activeCallFrame.sourceCodeLocation;
1096         if (!sourceCodeLocation)
1097             return;
1098
1099         const options = {
1100             useGoToArrowButton: true,
1101             ignoreNetworkTab: true,
1102             ignoreSearchTab: true,
1103         };
1104         let linkElement = WI.createSourceCodeLocationLink(sourceCodeLocation, options);
1105         this._pauseReasonLinkContainerElement.appendChild(linkElement);
1106     }
1107
1108     _addDebuggerObject(debuggerObject)
1109     {
1110         if (debuggerObject instanceof WI.Breakpoint)
1111             return this._addBreakpoint(debuggerObject);
1112
1113         if (debuggerObject instanceof WI.IssueMessage)
1114             return this._addIssue(debuggerObject);
1115
1116         return null;
1117     }
1118
1119     _addIssue(issueMessage)
1120     {
1121         let issueTreeElement = this._scriptsContentTreeOutline.findTreeElement(issueMessage);
1122         if (issueTreeElement)
1123             return issueTreeElement;
1124
1125         let parentTreeElement = this._addTreeElementForSourceCodeToTreeOutline(issueMessage.sourceCodeLocation.sourceCode, this._scriptsContentTreeOutline);
1126         if (!parentTreeElement)
1127             return null;
1128
1129         issueTreeElement = new WI.IssueTreeElement(issueMessage);
1130
1131         parentTreeElement.insertChild(issueTreeElement, insertionIndexForObjectInListSortedByFunction(issueTreeElement, parentTreeElement.children, this._compareTreeElements));
1132         if (parentTreeElement.children.length === 1)
1133             parentTreeElement.expand();
1134
1135         return issueTreeElement;
1136     }
1137
1138     _handleIssueAdded(event)
1139     {
1140         var issue = event.data.issue;
1141
1142         // We only want to show issues originating from JavaScript source code.
1143         if (!issue.sourceCodeLocation || !issue.sourceCodeLocation.sourceCode || (issue.source !== "javascript" && issue.source !== "console-api"))
1144             return;
1145
1146         this._addIssue(issue);
1147     }
1148
1149     _handleIssuesCleared(event)
1150     {
1151         let currentTreeElement = this._scriptsContentTreeOutline.children[0];
1152         let issueTreeElements = [];
1153
1154         while (currentTreeElement && !currentTreeElement.root) {
1155             if (currentTreeElement instanceof WI.IssueTreeElement)
1156                 issueTreeElements.push(currentTreeElement);
1157             currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true);
1158         }
1159
1160         issueTreeElements.forEach((treeElement) => treeElement.parent.removeChild(treeElement));
1161     }
1162
1163     _domBreakpointAddedOrRemoved(event)
1164     {
1165         if (!this._domBreakpointsContentTreeOutline.children.length) {
1166             this._domBreakpointsRow.showEmptyMessage();
1167             return;
1168         }
1169
1170         if (this._domBreakpointsContentTreeOutline.element.parent)
1171             return;
1172
1173         this._domBreakpointsRow.hideEmptyMessage();
1174         this._domBreakpointsRow.element.append(this._domBreakpointsContentTreeOutline.element);
1175
1176         this._domBreakpointsSection.collapsed = false;
1177     }
1178
1179     _addXHRBreakpointButtonClicked(event)
1180     {
1181         let popover = new WI.XHRBreakpointPopover(this);
1182         popover.show(event.target.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]);
1183     }
1184
1185     // Popover delegate
1186
1187     willDismissPopover(popover)
1188     {
1189         if (popover.result !== WI.InputPopover.Result.Committed)
1190             return;
1191
1192         let url = popover.value;
1193         if (!url)
1194             return;
1195
1196         WI.domDebuggerManager.addXHRBreakpoint(new WI.XHRBreakpoint(popover.type, url));
1197     }
1198 };
1199
1200 WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName = "paused";
1201 WI.DebuggerSidebarPanel.ExceptionIconStyleClassName = "breakpoint-exception-icon";
1202 WI.DebuggerSidebarPanel.AssertionIconStyleClassName = "breakpoint-assertion-icon";
1203 WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName = "breakpoint-paused-icon";
1204
1205 WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey = "debugger-sidebar-panel-all-exceptions-breakpoint";
1206 WI.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey = "debugger-sidebar-panel-all-uncaught-exceptions-breakpoint";
1207 WI.DebuggerSidebarPanel.SelectedAssertionsCookieKey = "debugger-sidebar-panel-assertions-breakpoint";
1208 WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey = "debugger-sidebar-panel-all-requests-breakpoint";