[LFC] LayoutContext::m_formattingContextRootListForLayout should be a WeakHashSet
[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()
29     {
30         super("debugger", WI.UIString("Debugger"), true);
31
32         WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
33         WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceAdded, this);
34         WI.Target.addEventListener(WI.Target.Event.ResourceAdded, this._resourceAdded, this);
35
36         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this);
37         WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.DOMBreakpointAdded, this._breakpointAdded, this);
38         WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.EventBreakpointAdded, this._breakpointAdded, this);
39         WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.URLBreakpointAdded, this._breakpointAdded, this);
40
41         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this);
42         WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, this._breakpointRemoved, this);
43         WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.EventBreakpointRemoved, this._breakpointRemoved, this);
44         WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.URLBreakpointRemoved, this._breakpointRemoved, this);
45
46         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
47         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this);
48         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptRemoved, this._scriptRemoved, this);
49         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptsCleared, this._scriptsCleared, this);
50         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
51         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
52         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.CallFramesDidChange, this._debuggerCallFramesDidChange, this);
53         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this._debuggerActiveCallFrameDidChange, this);
54         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.WaitingToPause, this._debuggerWaitingToPause, this);
55
56         WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeChanged, this._handleDOMBreakpointDOMNodeChanged, this);
57
58         WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this);
59
60         WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditManagerTestScheduled, this);
61         WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditManagerTestCompleted, this);
62
63         WI.targetManager.addEventListener(WI.TargetManager.Event.TargetAdded, this._targetAdded, this);
64         WI.targetManager.addEventListener(WI.TargetManager.Event.TargetRemoved, this._targetRemoved, this);
65
66         this._timelineRecordingWarningElement = document.createElement("div");
67         this._timelineRecordingWarningElement.classList.add("warning-banner");
68         this._timelineRecordingWarningElement.append(WI.UIString("Debugger disabled during Timeline recording"), document.createElement("br"));
69         let timelineStopRecordingLink = this._timelineRecordingWarningElement.appendChild(document.createElement("a"));
70         timelineStopRecordingLink.textContent = WI.UIString("Stop recording");
71         timelineStopRecordingLink.addEventListener("click", () => { WI.timelineManager.stopCapturing(); });
72
73         this._auditTestWarningElement = document.createElement("div");
74         this._auditTestWarningElement.classList.add("warning-banner");
75         this._auditTestWarningElement.append(WI.UIString("Debugger disabled during Audit"), document.createElement("br"));
76         let auditStopRecordingLink = this._auditTestWarningElement.appendChild(document.createElement("a"));
77         auditStopRecordingLink.textContent = WI.UIString("Stop Audit");
78         auditStopRecordingLink.addEventListener("click", () => { WI.auditManager.stop(); });
79
80         this._breakpointsDisabledWarningElement = document.createElement("div");
81         this._breakpointsDisabledWarningElement.classList.add("warning-banner");
82         this._breakpointsDisabledWarningElement.append(WI.UIString("Breakpoints disabled"), document.createElement("br"));
83         let enableBreakpointsLink = this._breakpointsDisabledWarningElement.appendChild(document.createElement("a"));
84         enableBreakpointsLink.textContent = WI.UIString("Enable breakpoints");
85         enableBreakpointsLink.addEventListener("click", () => { WI.debuggerToggleBreakpoints(); });
86
87         this._navigationBar = new WI.NavigationBar;
88         this.addSubview(this._navigationBar);
89
90         var breakpointsImage = {src: "Images/Breakpoints.svg", width: 15, height: 15};
91         var pauseImage = {src: "Images/Pause.svg", width: 15, height: 15};
92         var resumeImage = {src: "Images/Resume.svg", width: 15, height: 15};
93         var stepOverImage = {src: "Images/StepOver.svg", width: 15, height: 15};
94         var stepIntoImage = {src: "Images/StepInto.svg", width: 15, height: 15};
95         var stepOutImage = {src: "Images/StepOut.svg", width: 15, height: 15};
96
97         var toolTip = WI.UIString("Enable all breakpoints (%s)").format(WI.toggleBreakpointsKeyboardShortcut.displayName);
98         var altToolTip = WI.UIString("Disable all breakpoints (%s)").format(WI.toggleBreakpointsKeyboardShortcut.displayName);
99
100         this._debuggerBreakpointsButtonItem = new WI.ActivateButtonNavigationItem("debugger-breakpoints", toolTip, altToolTip, breakpointsImage.src, breakpointsImage.width, breakpointsImage.height);
101         this._debuggerBreakpointsButtonItem.activated = WI.debuggerManager.breakpointsEnabled;
102         this._debuggerBreakpointsButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerToggleBreakpoints, this);
103         this._navigationBar.addNavigationItem(this._debuggerBreakpointsButtonItem);
104
105         toolTip = WI.UIString("Pause script execution (%s or %s)").format(WI.pauseOrResumeKeyboardShortcut.displayName, WI.pauseOrResumeAlternateKeyboardShortcut.displayName);
106         altToolTip = WI.UIString("Continue script execution (%s or %s)").format(WI.pauseOrResumeKeyboardShortcut.displayName, WI.pauseOrResumeAlternateKeyboardShortcut.displayName);
107
108         this._debuggerPauseResumeButtonItem = new WI.ToggleButtonNavigationItem("debugger-pause-resume", toolTip, altToolTip, pauseImage.src, resumeImage.src, pauseImage.width, pauseImage.height);
109         this._debuggerPauseResumeButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerPauseResumeToggle, this);
110         this._navigationBar.addNavigationItem(this._debuggerPauseResumeButtonItem);
111
112         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);
113         this._debuggerStepOverButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepOver, this);
114         this._debuggerStepOverButtonItem.enabled = false;
115         this._navigationBar.addNavigationItem(this._debuggerStepOverButtonItem);
116
117         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);
118         this._debuggerStepIntoButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepInto, this);
119         this._debuggerStepIntoButtonItem.enabled = false;
120         this._navigationBar.addNavigationItem(this._debuggerStepIntoButtonItem);
121
122         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);
123         this._debuggerStepOutButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepOut, this);
124         this._debuggerStepOutButtonItem.enabled = false;
125         this._navigationBar.addNavigationItem(this._debuggerStepOutButtonItem);
126
127         function showResourcesWithIssuesOnlyFilterFunction(treeElement)
128         {
129             // Issues are only shown in the scripts tree outline.
130             if (treeElement.treeOutline !== this._scriptsContentTreeOutline)
131                 return true;
132
133             // Keep issues.
134             if (treeElement instanceof WI.IssueTreeElement)
135                 return true;
136
137             // Keep resources with issues.
138             if (treeElement.hasChildren) {
139                 for (let child of treeElement.children) {
140                     if (child instanceof WI.IssueTreeElement)
141                         return true;
142                 }
143             }
144             return false;
145         }
146
147         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);
148
149         this._breakpointsContentTreeOutline = this.contentTreeOutline;
150
151         let breakpointsRow = new WI.DetailsSectionRow;
152         breakpointsRow.element.appendChild(this._breakpointsContentTreeOutline.element);
153
154         let breakpointNavigationBarWrapper = document.createElement("div");
155
156         let breakpointNavigationBar = new WI.NavigationBar;
157         breakpointNavigationBarWrapper.appendChild(breakpointNavigationBar.element);
158
159         this._createBreakpointButton = new WI.ButtonNavigationItem("create-breakpoint", WI.UIString("Create Breakpoint"), "Images/Plus13.svg", 13, 13);
160         WI.addMouseDownContextMenuHandlers(this._createBreakpointButton.element, this._populateCreateBreakpointContextMenu.bind(this));
161         breakpointNavigationBar.addNavigationItem(this._createBreakpointButton);
162
163         let breakpointsGroup = new WI.DetailsSectionGroup([breakpointsRow]);
164         this._breakpointsSection = new WI.DetailsSection("breakpoints", WI.UIString("Breakpoints"), [breakpointsGroup], breakpointNavigationBarWrapper);
165         this.contentView.element.appendChild(this._breakpointsSection.element);
166
167         this._breakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.ElementAdded, this._handleBreakpointElementAddedOrRemoved, this);
168         this._breakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.ElementRemoved, this._handleBreakpointElementAddedOrRemoved, this);
169         this._breakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
170         this._breakpointsContentTreeOutline.ondelete = this._breakpointTreeOutlineDeleteTreeElement.bind(this);
171         this._breakpointsContentTreeOutline.populateContextMenu = function(contextMenu, event, treeElement) {
172             this._breakpointTreeOutlineContextMenuTreeElement(contextMenu, event, treeElement);
173
174             WI.TreeOutline.prototype.populateContextMenu(contextMenu, event, treeElement);
175         }.bind(this);
176
177         this._scriptsContentTreeOutline = this.createContentTreeOutline();
178         this._scriptsContentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
179         this._scriptsContentTreeOutline.includeSourceMapResourceChildren = true;
180
181         let scriptsRow = new WI.DetailsSectionRow;
182         scriptsRow.element.appendChild(this._scriptsContentTreeOutline.element);
183
184         let scriptsGroup = new WI.DetailsSectionGroup([scriptsRow]);
185         this._scriptsSection = new WI.DetailsSection("scripts", WI.UIString("Sources"), [scriptsGroup]);
186         this.contentView.element.appendChild(this._scriptsSection.element);
187
188         this._callStackTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
189         this._callStackTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
190
191         this._callStackRow = new WI.DetailsSectionRow;
192         this._callStackRow.element.appendChild(this._callStackTreeOutline.element);
193
194         this._callStackGroup = new WI.DetailsSectionGroup([this._callStackRow]);
195         this._callStackSection = new WI.DetailsSection("call-stack", WI.UIString("Call Stack"), [this._callStackGroup]);
196
197         this._mainTargetTreeElement = null;
198         this._activeCallFrameTreeElement = null;
199
200         this._pauseReasonTreeOutline = null;
201
202         this._pauseReasonLinkContainerElement = document.createElement("span");
203         this._pauseReasonTextRow = new WI.DetailsSectionTextRow;
204         this._pauseReasonGroup = new WI.DetailsSectionGroup([this._pauseReasonTextRow]);
205         this._pauseReasonSection = new WI.DetailsSection("paused-reason", WI.UIString("Pause Reason"), [this._pauseReasonGroup], this._pauseReasonLinkContainerElement);
206
207         WI.Breakpoint.addEventListener(WI.Breakpoint.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
208         WI.IssueMessage.addEventListener(WI.IssueMessage.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
209         WI.consoleManager.addEventListener(WI.ConsoleManager.Event.IssueAdded, this._handleIssueAdded, this);
210         WI.consoleManager.addEventListener(WI.ConsoleManager.Event.Cleared, this._handleIssuesCleared, this);
211
212         WI.debuggerManager.addBreakpoint(WI.debuggerManager.allExceptionsBreakpoint);
213         WI.debuggerManager.addBreakpoint(WI.debuggerManager.uncaughtExceptionsBreakpoint);
214
215         // COMPATIBILITY (iOS 10): DebuggerAgent.setPauseOnAssertions did not exist yet.
216         if (InspectorBackend.domains.Debugger.setPauseOnAssertions && WI.settings.showAssertionFailuresBreakpoint.value)
217             WI.debuggerManager.addBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint);
218
219         // COMPATIBILITY (iOS 13): DebuggerAgent.setPauseOnMicrotasks did not exist yet.
220         if (InspectorBackend.domains.Debugger.setPauseOnMicrotasks && WI.settings.showAllMicrotasksBreakpoint.value)
221             WI.debuggerManager.addBreakpoint(WI.debuggerManager.allMicrotasksBreakpoint);
222
223         for (let target of WI.targets)
224             this._addTarget(target);
225         this._updateCallStackTreeOutline();
226
227         if (WI.networkManager.mainFrame)
228             this._addResourcesRecursivelyForFrame(WI.networkManager.mainFrame);
229
230         for (let script of WI.debuggerManager.knownNonResourceScripts)
231             this._addScript(script);
232
233         if (WI.domDebuggerManager.supported) {
234             if (WI.settings.showAllAnimationFramesBreakpoint.value)
235                 WI.domDebuggerManager.addEventBreakpoint(WI.domDebuggerManager.allAnimationFramesBreakpoint);
236
237             if (WI.settings.showAllTimeoutsBreakpoint.value)
238                 WI.domDebuggerManager.addEventBreakpoint(WI.domDebuggerManager.allTimeoutsBreakpoint);
239
240             if (WI.settings.showAllIntervalsBreakpoint.value)
241                 WI.domDebuggerManager.addEventBreakpoint(WI.domDebuggerManager.allIntervalsBreakpoint);
242
243             if (WI.settings.showAllListenersBreakpoint.value)
244                 WI.domDebuggerManager.addEventBreakpoint(WI.domDebuggerManager.allListenersBreakpoint);
245
246             for (let eventBreakpoint of WI.domDebuggerManager.listenerBreakpoints)
247                 this._addBreakpoint(eventBreakpoint);
248
249             for (let eventListenerBreakpoint of WI.domManager.eventListenerBreakpoints)
250                 this._addBreakpoint(eventListenerBreakpoint);
251
252             for (let domBreakpoint of WI.domDebuggerManager.domBreakpoints)
253                 this._addBreakpoint(domBreakpoint);
254
255             if (WI.settings.showAllRequestsBreakpoint.value)
256                 WI.domDebuggerManager.addURLBreakpoint(WI.domDebuggerManager.allRequestsBreakpoint);
257
258             for (let urlBreakpoints of WI.domDebuggerManager.urlBreakpoints)
259                 this._addBreakpoint(urlBreakpoints);
260         }
261
262         if (WI.debuggerManager.paused)
263             this._debuggerDidPause(null);
264
265         if (WI.debuggerManager.breakpointsDisabledTemporarily) {
266             this._handleTimelineCapturingStateChanged();
267
268             if (WI.auditManager.runningState === WI.AuditManager.RunningState.Active || WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping)
269                 this._handleAuditManagerTestScheduled();
270         }
271
272         this._updateBreakpointsDisabledBanner();
273     }
274
275     // Public
276
277     get minimumWidth()
278     {
279         return this._navigationBar.minimumWidth;
280     }
281
282     closed()
283     {
284         super.closed();
285
286         WI.Frame.removeEventListener(null, null, this);
287         WI.Target.removeEventListener(null, null, this);
288         WI.debuggerManager.removeEventListener(null, null, this);
289         WI.domDebuggerManager.removeEventListener(null, null, this);
290         WI.DOMBreakpoint.removeEventListener(null, null, this);
291         WI.timelineManager.removeEventListener(null, null, this);
292         WI.targetManager.removeEventListener(null, null, this);
293         WI.Breakpoint.removeEventListener(null, null, this);
294         WI.IssueMessage.removeEventListener(null, null, this);
295     }
296
297     showDefaultContentView()
298     {
299         if (WI.networkManager.mainFrame) {
300             let mainTreeElement = this._scriptsContentTreeOutline.findTreeElement(WI.networkManager.mainFrame.mainResource);
301             if (mainTreeElement && this.showDefaultContentViewForTreeElement(mainTreeElement))
302                 return;
303         }
304
305         let currentTreeElement = this._scriptsContentTreeOutline.children[0];
306         while (currentTreeElement && !currentTreeElement.root) {
307             if (currentTreeElement instanceof WI.ResourceTreeElement || currentTreeElement instanceof WI.ScriptTreeElement) {
308                 if (this.showDefaultContentViewForTreeElement(currentTreeElement))
309                     return;
310             }
311
312             currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true);
313         }
314     }
315
316     treeElementForRepresentedObject(representedObject)
317     {
318         if (representedObject instanceof WI.LocalResource)
319             return null;
320
321         // The main resource is used as the representedObject instead of Frame in our tree.
322         if (representedObject instanceof WI.Frame)
323             representedObject = representedObject.mainResource;
324
325         let treeElement = this._breakpointsContentTreeOutline.findTreeElement(representedObject);
326         if (!treeElement)
327             treeElement = this._scriptsContentTreeOutline.findTreeElement(representedObject);
328
329         if (treeElement)
330             return treeElement;
331
332         // Only special case Script objects.
333         if (!(representedObject instanceof WI.Script)) {
334             console.error("Didn't find a TreeElement for representedObject", representedObject);
335             return null;
336         }
337
338         // If the Script has a URL we should have found it earlier.
339         if (representedObject.url) {
340             console.error("Didn't find a ScriptTreeElement for a Script with a URL.");
341             return null;
342         }
343
344         // Since the Script does not have a URL we consider it an 'anonymous' script. These scripts happen from calls to
345         // window.eval() or browser features like Auto Fill and Reader. They are not normally added to the sidebar, but since
346         // we have a ScriptContentView asking for the tree element we will make a ScriptTreeElement on demand and add it.
347
348         return this._addScript(representedObject);
349     }
350
351     createContentTreeOutline(options = {})
352     {
353         let treeOutline = super.createContentTreeOutline(options);
354
355         treeOutline.addEventListener(WI.TreeOutline.Event.ElementRevealed, (event) => {
356             let treeElement = event.data.element;
357             let detailsSections = [this._pauseReasonSection, this._callStackSection, this._breakpointsSection, this._scriptsSection];
358             let detailsSection = detailsSections.find((detailsSection) => detailsSection.element.contains(treeElement.listItemElement));
359             if (!detailsSection)
360                 return;
361
362             // Revealing a TreeElement at the scroll container's topmost edge with
363             // scrollIntoViewIfNeeded may result in the element being covered by the
364             // DetailsSection header, which uses sticky positioning. Detect this case,
365             // and adjust the sidebar content's scroll position to compensate.
366             let offset = detailsSection.headerElement.totalOffsetBottom - treeElement.listItemElement.totalOffsetTop;
367             if (offset > 0)
368                 this.scrollElement.scrollBy(0, -offset);
369         });
370
371         return treeOutline;
372     }
373
374     // Protected
375
376     saveStateToCookie(cookie)
377     {
378         console.assert(cookie);
379
380         var selectedTreeElement = this._breakpointsContentTreeOutline.selectedTreeElement;
381         if (!selectedTreeElement) {
382             super.saveStateToCookie(cookie);
383             return;
384         }
385
386         switch (selectedTreeElement.representedObject) {
387         case WI.debuggerManager.allExceptionsBreakpoint:
388             cookie[DebuggerSidebarPanel.SelectedAllExceptionsCookieKey] = true;
389             return;
390
391         case WI.debuggerManager.uncaughtExceptionsBreakpoint:
392             cookie[DebuggerSidebarPanel.SelectedUncaughtExceptionsCookieKey] = true;
393             return;
394
395         case WI.debuggerManager.assertionFailuresBreakpoint:
396             cookie[DebuggerSidebarPanel.SelectedAssertionFailuresCookieKey] = true;
397             return;
398
399         case WI.debuggerManager.allMicrotasksBreakpoint:
400             cookie[DebuggerSidebarPanel.SelectedAllMicrotasksCookieKey] = true;
401             return;
402
403         case WI.domDebuggerManager.allAnimationFramesBreakpoint:
404             cookie[DebuggerSidebarPanel.SelectedAllAnimationFramesBreakpoint] = true;
405             return;
406
407         case WI.domDebuggerManager.allIntervalsBreakpoint:
408             cookie[DebuggerSidebarPanel.SelectedAllIntervalsBreakpoint] = true;
409             return;
410
411         case WI.domDebuggerManager.allListenersBreakpoint:
412             cookie[DebuggerSidebarPanel.SelectedAllListenersBreakpoint] = true;
413             return;
414
415         case WI.domDebuggerManager.allTimeoutsBreakpoint:
416             cookie[DebuggerSidebarPanel.SelectedAllTimeoutsBreakpoint] = true;
417             return;
418
419         case WI.domDebuggerManager.allRequestsBreakpoint:
420             cookie[DebuggerSidebarPanel.SelectedAllRequestsCookieKey] = true;
421             return;
422         }
423
424         super.saveStateToCookie(cookie);
425     }
426
427     restoreStateFromCookie(cookie, relaxedMatchDelay)
428     {
429         console.assert(cookie);
430
431         function revealAndSelect(treeOutline, breakpoint) {
432             let treeElement = treeOutline.findTreeElement(breakpoint);
433             if (!treeElement)
434                 return;
435
436             treeElement.revealAndSelect();
437         }
438
439         // Eagerly resolve the special breakpoints; otherwise, use the default behavior.
440         if (cookie[DebuggerSidebarPanel.SelectedAllExceptionsCookieKey])
441             revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.allExceptionsBreakpoint);
442         else if (cookie[DebuggerSidebarPanel.SelectedUncaughtExceptionsCookieKey])
443             revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.uncaughtExceptionsBreakpoint);
444         else if (cookie[DebuggerSidebarPanel.SelectedAssertionFailuresCookieKey])
445             revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.assertionFailuresBreakpoint);
446         else if (cookie[DebuggerSidebarPanel.SelectedAllMicrotasksCookieKey])
447             revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.allMicrotasksBreakpoint);
448         else if (cookie[DebuggerSidebarPanel.SelectedAllAnimationFramesBreakpoint])
449             revealAndSelect(this._breakpointsContentTreeOutline, WI.domDebuggerManager.allAnimationFramesBreakpoint);
450         else if (cookie[DebuggerSidebarPanel.SelectedAllIntervalsBreakpoint])
451             revealAndSelect(this._breakpointsContentTreeOutline, WI.domDebuggerManager.allIntervalsBreakpoint);
452         else if (cookie[DebuggerSidebarPanel.SelectedAllListenersBreakpoint])
453             revealAndSelect(this._breakpointsContentTreeOutline, WI.domDebuggerManager.allListenersBreakpoint);
454         else if (cookie[DebuggerSidebarPanel.SelectedAllRequestsCookieKey])
455             revealAndSelect(this._breakpointsContentTreeOutline, WI.domDebuggerManager.allRequestsBreakpoint);
456         else if (cookie[DebuggerSidebarPanel.SelectedAllTimeoutsBreakpoint])
457             revealAndSelect(this._breakpointsContentTreeOutline, WI.domDebuggerManager.allTimeoutsBreakpoint);
458         else
459             super.restoreStateFromCookie(cookie, relaxedMatchDelay);
460     }
461
462     // Popover delegate
463
464     willDismissPopover(popover)
465     {
466         let breakpoint = popover.breakpoint;
467         if (!breakpoint)
468             return;
469
470         if (breakpoint instanceof WI.EventBreakpoint)
471             WI.domDebuggerManager.addEventBreakpoint(breakpoint);
472         else if (breakpoint instanceof WI.URLBreakpoint)
473             WI.domDebuggerManager.addURLBreakpoint(breakpoint);
474     }
475
476     // Private
477
478     _debuggerWaitingToPause(event)
479     {
480         this._debuggerPauseResumeButtonItem.enabled = false;
481     }
482
483     _debuggerDidPause(event)
484     {
485         this.contentView.element.insertBefore(this._callStackSection.element, this.contentView.element.firstChild);
486
487         if (this._updatePauseReason())
488             this.contentView.element.insertBefore(this._pauseReasonSection.element, this.contentView.element.firstChild);
489
490         this._debuggerPauseResumeButtonItem.enabled = true;
491         this._debuggerPauseResumeButtonItem.toggled = true;
492         this._debuggerStepOverButtonItem.enabled = true;
493         this._debuggerStepIntoButtonItem.enabled = true;
494         this._debuggerStepOutButtonItem.enabled = true;
495
496         this.element.classList.add(WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName);
497     }
498
499     _debuggerDidResume(event)
500     {
501         this._callStackSection.element.remove();
502
503         this._pauseReasonSection.element.remove();
504
505         this._debuggerPauseResumeButtonItem.enabled = true;
506         this._debuggerPauseResumeButtonItem.toggled = false;
507         this._debuggerStepOverButtonItem.enabled = false;
508         this._debuggerStepIntoButtonItem.enabled = false;
509         this._debuggerStepOutButtonItem.enabled = false;
510
511         this.element.classList.remove(WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName);
512     }
513
514     _breakpointsEnabledDidChange(event)
515     {
516         this._debuggerBreakpointsButtonItem.activated = WI.debuggerManager.breakpointsEnabled;
517
518         this._updateBreakpointsDisabledBanner();
519     }
520
521     _addBreakpoint(breakpoint)
522     {
523         let constructor = WI.BreakpointTreeElement;
524         let options = {};
525         let parentTreeElement = this._breakpointsContentTreeOutline;
526
527         let getDOMNodeTreeElement = (domNode) => {
528             console.assert(domNode, "Missing DOMNode for identifier", breakpoint.domNodeIdentifier);
529             if (!domNode)
530                 return null;
531
532             let domNodeTreeElement = this._breakpointsContentTreeOutline.findTreeElement(domNode);
533             if (!domNodeTreeElement) {
534                 domNodeTreeElement = new WI.DOMNodeTreeElement(domNode);
535                 this._addTreeElement(domNodeTreeElement, parentTreeElement);
536             }
537             return domNodeTreeElement;
538         };
539
540         if (breakpoint === WI.debuggerManager.allExceptionsBreakpoint) {
541             options.className = WI.DebuggerSidebarPanel.ExceptionIconStyleClassName;
542             options.title = WI.repeatedUIString.allExceptions();
543         } else if (breakpoint === WI.debuggerManager.uncaughtExceptionsBreakpoint) {
544             options.className = WI.DebuggerSidebarPanel.ExceptionIconStyleClassName;
545             options.title = WI.repeatedUIString.uncaughtExceptions();
546         } else if (breakpoint === WI.debuggerManager.assertionFailuresBreakpoint) {
547             options.className = WI.DebuggerSidebarPanel.AssertionIconStyleClassName;
548             options.title = WI.repeatedUIString.assertionFailures();
549         } else if (breakpoint === WI.debuggerManager.allMicrotasksBreakpoint) {
550             options.className = "breakpoint-microtask-icon";
551             options.title = WI.UIString("All Microtasks");
552         } else if (breakpoint instanceof WI.DOMBreakpoint) {
553             if (!breakpoint.domNodeIdentifier)
554                 return null;
555
556             constructor = WI.DOMBreakpointTreeElement;
557
558             let domNode = WI.domManager.nodeForId(breakpoint.domNodeIdentifier);
559             parentTreeElement = getDOMNodeTreeElement(domNode);
560         } else if (breakpoint instanceof WI.EventBreakpoint) {
561             constructor = WI.EventBreakpointTreeElement;
562
563             if (breakpoint === WI.domDebuggerManager.allAnimationFramesBreakpoint)
564                 options.title = WI.UIString("All Animation Frames");
565             else if (breakpoint === WI.domDebuggerManager.allIntervalsBreakpoint)
566                 options.title = WI.UIString("All Intervals");
567             else if (breakpoint === WI.domDebuggerManager.allListenersBreakpoint)
568                 options.title = WI.UIString("All Events");
569             else if (breakpoint === WI.domDebuggerManager.allTimeoutsBreakpoint)
570                 options.title = WI.UIString("All Timeouts");
571             else if (breakpoint.eventListener) {
572                 let eventTargetTreeElement = null;
573                 if (breakpoint.eventListener.onWindow) {
574                     if (!DebuggerSidebarPanel.__windowEventTargetRepresentedObject)
575                         DebuggerSidebarPanel.__windowEventTargetRepresentedObject = {__window: true};
576
577                     eventTargetTreeElement = this._breakpointsContentTreeOutline.findTreeElement(DebuggerSidebarPanel.__windowEventTargetRepresentedObject);
578                     if (!eventTargetTreeElement) {
579                         const subtitle = null;
580                         eventTargetTreeElement = new WI.GeneralTreeElement(["event-target-window"], WI.unlocalizedString("window"), subtitle, DebuggerSidebarPanel.__windowEventTargetRepresentedObject);
581                         this._addTreeElement(eventTargetTreeElement, parentTreeElement);
582                     }
583                 } else if (breakpoint.eventListener.node)
584                     eventTargetTreeElement = getDOMNodeTreeElement(breakpoint.eventListener.node);
585                 if (eventTargetTreeElement)
586                     parentTreeElement = eventTargetTreeElement;
587             }
588         } else if (breakpoint instanceof WI.URLBreakpoint) {
589             constructor = WI.URLBreakpointTreeElement;
590
591             if (breakpoint === WI.domDebuggerManager.allRequestsBreakpoint)
592                 options.title = WI.repeatedUIString.allRequests();
593         } else {
594             let sourceCode = breakpoint.sourceCodeLocation && breakpoint.sourceCodeLocation.displaySourceCode;
595             if (!sourceCode)
596                 return null;
597
598             if (this._breakpointsContentTreeOutline.findTreeElement(breakpoint))
599                 return null;
600
601             parentTreeElement = this._addTreeElementForSourceCodeToTreeOutline(sourceCode, this._breakpointsContentTreeOutline);
602
603             // Mark disabled breakpoints as resolved if there is source code loaded with that URL.
604             // This gives the illusion the breakpoint was resolved, but since we don't send disabled
605             // breakpoints to the backend we don't know for sure. If the user enables the breakpoint
606             // it will be resolved properly.
607             if (breakpoint.disabled)
608                 breakpoint.resolved = true;
609         }
610
611         let breakpointTreeElement = new constructor(breakpoint, options);
612         this._addTreeElement(breakpointTreeElement, parentTreeElement);
613         if (parentTreeElement.children.length === 1)
614             parentTreeElement.expand();
615         return breakpointTreeElement;
616     }
617
618     _removeBreakpoint(breakpoint)
619     {
620         if (this._pauseReasonTreeOutline) {
621             let pauseReasonBreakpointTreeElement = this._pauseReasonTreeOutline.getCachedTreeElement(breakpoint);
622             if (pauseReasonBreakpointTreeElement)
623                 pauseReasonBreakpointTreeElement.status = null;
624         }
625
626         let breakpointTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(breakpoint);
627         if (!breakpointTreeElement)
628             return;
629
630         this._removeDebuggerTreeElement(breakpointTreeElement);
631     }
632
633     _addBreakpointsForSourceCode(sourceCode)
634     {
635         for (let breakpoint of WI.debuggerManager.breakpointsForSourceCode(sourceCode))
636             this._addBreakpoint(breakpoint, sourceCode);
637     }
638
639     _addIssuesForSourceCode(sourceCode)
640     {
641         var issues = WI.consoleManager.issuesForSourceCode(sourceCode);
642         for (var issue of issues)
643             this._addIssue(issue, sourceCode);
644     }
645
646     _addTreeElementForSourceCodeToTreeOutline(sourceCode, treeOutline)
647     {
648         let treeElement = treeOutline.getCachedTreeElement(sourceCode);
649         if (!treeElement) {
650             if (sourceCode instanceof WI.SourceMapResource)
651                 treeElement = new WI.SourceMapResourceTreeElement(sourceCode);
652             else if (sourceCode instanceof WI.Resource)
653                 treeElement = new WI.ResourceTreeElement(sourceCode);
654             else if (sourceCode instanceof WI.Script)
655                 treeElement = new WI.ScriptTreeElement(sourceCode);
656         }
657
658         if (!treeElement) {
659             console.error("Unknown sourceCode instance", sourceCode);
660             return null;
661         }
662
663         if (!treeElement.parent) {
664             treeElement.hasChildren = false;
665             treeElement.expand();
666
667             this._addTreeElement(treeElement, treeOutline);
668         }
669
670         return treeElement;
671     }
672
673     _addResourcesRecursivelyForFrame(frame)
674     {
675         this._addResource(frame.mainResource);
676
677         for (let resource of frame.resourceCollection)
678             this._addResource(resource);
679
680         for (let childFrame of frame.childFrameCollection)
681             this._addResourcesRecursivelyForFrame(childFrame);
682     }
683
684     _resourceAdded(event)
685     {
686         this._addResource(event.data.resource);
687     }
688
689     _addResource(resource)
690     {
691         if (![WI.Resource.Type.Document, WI.Resource.Type.Script].includes(resource.type))
692             return;
693
694         let treeElement = this._addTreeElementForSourceCodeToTreeOutline(resource, this._scriptsContentTreeOutline);
695         this._addBreakpointsForSourceCode(resource);
696         this._addIssuesForSourceCode(resource);
697
698         if (this.parentSidebar && !this.contentBrowser.currentContentView)
699             this.showDefaultContentViewForTreeElement(treeElement);
700     }
701
702     _mainResourceDidChange(event)
703     {
704         if (event.target.isMainFrame()) {
705             // Aggressively prune resources now so the old resources are removed before
706             // the new main resource is added below. This avoids a visual flash when the
707             // prune normally happens on a later event loop cycle.
708             this.pruneStaleResourceTreeElements();
709             this.contentBrowser.contentViewContainer.closeAllContentViews();
710
711             for (let domBreakpoint of WI.domDebuggerManager.domBreakpoints)
712                 this._removeBreakpoint(domBreakpoint);
713         }
714
715         if (!event.data.oldMainResource) {
716             var resource = event.target.mainResource;
717             this._addTreeElementForSourceCodeToTreeOutline(resource, this._scriptsContentTreeOutline);
718             this._addBreakpointsForSourceCode(resource);
719             this._addIssuesForSourceCode(resource);
720         }
721     }
722
723     _handleTimelineCapturingStateChanged(event)
724     {
725         this._updateTemporarilyDisabledBreakpointsButtons();
726
727         switch (WI.timelineManager.capturingState) {
728         case WI.TimelineManager.CapturingState.Starting:
729             this.contentView.element.insertBefore(this._timelineRecordingWarningElement, this.contentView.element.firstChild);
730             break;
731
732         case WI.TimelineManager.CapturingState.Inactive:
733             this._timelineRecordingWarningElement.remove();
734             break;
735         }
736
737         this._updateBreakpointsDisabledBanner();
738     }
739
740     _handleAuditManagerTestScheduled(event)
741     {
742         this._updateTemporarilyDisabledBreakpointsButtons();
743
744         this.contentView.element.insertBefore(this._auditTestWarningElement, this.contentView.element.firstChild);
745         this._updateBreakpointsDisabledBanner();
746     }
747
748     _handleAuditManagerTestCompleted(event)
749     {
750         this._updateTemporarilyDisabledBreakpointsButtons();
751
752         this._auditTestWarningElement.remove();
753         this._updateBreakpointsDisabledBanner();
754     }
755
756     _updateTemporarilyDisabledBreakpointsButtons()
757     {
758         let breakpointsDisabledTemporarily = WI.debuggerManager.breakpointsDisabledTemporarily;
759         this._debuggerBreakpointsButtonItem.enabled = !breakpointsDisabledTemporarily;
760         this._debuggerPauseResumeButtonItem.enabled = !breakpointsDisabledTemporarily;
761     }
762
763     _updateBreakpointsDisabledBanner()
764     {
765         let breakpointsEnabled = WI.debuggerManager.breakpointsEnabled;
766         let timelineWarningShowing = !!this._timelineRecordingWarningElement.parentElement;
767         let auditWarningShowing = !!this._auditTestWarningElement.parentElement;
768
769         if (!breakpointsEnabled && !timelineWarningShowing && !auditWarningShowing)
770             this.contentView.element.insertBefore(this._breakpointsDisabledWarningElement, this.contentView.element.firstChild);
771         else
772             this._breakpointsDisabledWarningElement.remove();
773     }
774
775     _scriptAdded(event)
776     {
777         this._addScript(event.data.script);
778     }
779
780     _addScript(script)
781     {
782         // COMPATIBILITY(iOS 9): Backends could send the frontend built-in code, filter out JSC internals.
783         if (!script.url && !script.sourceURL)
784             return null;
785
786         // In general, do not show dynamically added script elements.
787         if (script.dynamicallyAddedScriptElement && !script.sourceURL)
788             return null;
789
790         // Don't add breakpoints if the script is represented by a Resource. They were
791         // already added by _resourceAdded.
792         if (script.resource)
793             return null;
794
795         let treeElement = this._addTreeElementForSourceCodeToTreeOutline(script, this._scriptsContentTreeOutline);
796         this._addBreakpointsForSourceCode(script);
797         this._addIssuesForSourceCode(script);
798
799         if (this.parentSidebar && !this.contentBrowser.currentContentView)
800             this.showDefaultContentViewForTreeElement(treeElement);
801
802         return treeElement;
803     }
804
805     _scriptRemoved(event)
806     {
807         function removeScript(script, treeOutline)
808         {
809             let scriptTreeElement = treeOutline.getCachedTreeElement(script);
810             if (scriptTreeElement)
811                 scriptTreeElement.parent.removeChild(scriptTreeElement);
812         }
813
814         let script = event.data.script;
815         removeScript(script, this._breakpointsContentTreeOutline);
816         removeScript(script, this._scriptsContentTreeOutline);
817     }
818
819     _scriptsCleared(event)
820     {
821         for (var i = this._breakpointsContentTreeOutline.children.length - 1; i >= 0; --i) {
822             var treeElement = this._breakpointsContentTreeOutline.children[i];
823             if (!(treeElement instanceof WI.ScriptTreeElement))
824                 continue;
825
826             this._breakpointsContentTreeOutline.removeChildAtIndex(i, true, true);
827         }
828
829         this._scriptsContentTreeOutline.removeChildren();
830
831         this._addResourcesRecursivelyForFrame(WI.networkManager.mainFrame);
832     }
833
834     _breakpointAdded(event)
835     {
836         var breakpoint = event.data.breakpoint;
837         this._addBreakpoint(breakpoint);
838     }
839
840     _breakpointRemoved(event)
841     {
842         var breakpoint = event.data.breakpoint;
843         this._removeBreakpoint(breakpoint);
844     }
845
846     _findThreadTreeElementForTarget(target)
847     {
848         for (let child of this._callStackTreeOutline.children) {
849             if (child.target === target)
850                 return child;
851         }
852
853         return null;
854     }
855
856     _targetAdded(event)
857     {
858         this._addTarget(event.data.target);
859     }
860
861     _addTarget(target)
862     {
863         let treeElement = new WI.ThreadTreeElement(target);
864         this._callStackTreeOutline.appendChild(treeElement);
865
866         // FIXME: When WI.mainTarget changes?
867         if (target === WI.mainTarget)
868             this._mainTargetTreeElement = treeElement;
869
870         this._updateCallStackTreeOutline();
871     }
872
873     _targetRemoved(event)
874     {
875         let target = event.data.target;
876         let treeElement = this._findThreadTreeElementForTarget(target);
877         this._callStackTreeOutline.removeChild(treeElement);
878
879         this._updateCallStackTreeOutline();
880     }
881
882     _updateCallStackTreeOutline()
883     {
884         let singleThreadShowing = WI.targets.length <= 1;
885         this._callStackTreeOutline.element.classList.toggle("single-thread", singleThreadShowing);
886         if (this._mainTargetTreeElement)
887             this._mainTargetTreeElement.selectable = !singleThreadShowing;
888     }
889
890     _handleDebuggerObjectDisplayLocationDidChange(event)
891     {
892         var debuggerObject = event.target;
893
894         if (event.data.oldDisplaySourceCode === debuggerObject.sourceCodeLocation.displaySourceCode)
895             return;
896
897         // A known debugger object (breakpoint, issueMessage, etc.) moved between resources. Remove
898         // the old tree element and create a new tree element with the updated file.
899
900         let wasSelected = false;
901         let oldDebuggerTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(debuggerObject);
902         if (oldDebuggerTreeElement)
903             wasSelected = oldDebuggerTreeElement.selected;
904
905         let newDebuggerTreeElement = null;
906         if (debuggerObject instanceof WI.Breakpoint)
907             newDebuggerTreeElement = this._addBreakpoint(debuggerObject);
908         else if (debuggerObject instanceof WI.IssueMessage)
909             newDebuggerTreeElement = this._addIssue(debuggerObject);
910         if (!newDebuggerTreeElement)
911             return;
912
913         if (oldDebuggerTreeElement)
914             this._removeDebuggerTreeElement(oldDebuggerTreeElement);
915
916         if (wasSelected)
917             newDebuggerTreeElement.revealAndSelect(true, false, true);
918     }
919
920     _removeDebuggerTreeElement(debuggerTreeElement)
921     {
922         // If this is a BreakpointTreeElement being deleted because of a cause
923         // outside of the TreeOutline then deselect if it is selected to avoid
924         // TreeOutline selection changes causing unexpected ContentView changes.
925         if (!debuggerTreeElement.__deletedViaDeleteKeyboardShortcut)
926             debuggerTreeElement.deselect();
927
928         let parentTreeElement = debuggerTreeElement.parent;
929         parentTreeElement.removeChild(debuggerTreeElement);
930
931         if (parentTreeElement.children.length || parentTreeElement === this._breakpointsContentTreeOutline)
932             return;
933
934         parentTreeElement.treeOutline.removeChild(parentTreeElement);
935     }
936
937     _debuggerCallFramesDidChange(event)
938     {
939         let target = event.data.target;
940         let treeElement = this._findThreadTreeElementForTarget(target);
941         treeElement.refresh();
942
943         let activeCallFrameTreeElement = this._callStackTreeOutline.findTreeElement(WI.debuggerManager.activeCallFrame);
944         if (activeCallFrameTreeElement)
945             activeCallFrameTreeElement.reveal();
946     }
947
948     _debuggerActiveCallFrameDidChange()
949     {
950         if (this._activeCallFrameTreeElement) {
951             this._activeCallFrameTreeElement.isActiveCallFrame = false;
952             this._activeCallFrameTreeElement = null;
953         }
954
955         if (!WI.debuggerManager.activeCallFrame)
956             return;
957
958         this._activeCallFrameTreeElement = this._callStackTreeOutline.findTreeElement(WI.debuggerManager.activeCallFrame);
959         if (this._activeCallFrameTreeElement)
960             this._activeCallFrameTreeElement.isActiveCallFrame = true;
961     }
962
963     _breakpointsBeneathTreeElement(treeElement)
964     {
965         console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement);
966         if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
967             return [];
968
969         var breakpoints = [];
970         var breakpointTreeElements = treeElement.children;
971         for (var i = 0; i < breakpointTreeElements.length; ++i) {
972             console.assert(breakpointTreeElements[i] instanceof WI.BreakpointTreeElement);
973             console.assert(breakpointTreeElements[i].breakpoint);
974             var breakpoint = breakpointTreeElements[i].breakpoint;
975             if (breakpoint)
976                 breakpoints.push(breakpoint);
977         }
978
979         return breakpoints;
980     }
981
982     _removeAllBreakpoints(breakpoints)
983     {
984         for (var i = 0; i < breakpoints.length; ++i) {
985             var breakpoint = breakpoints[i];
986             if (WI.debuggerManager.isBreakpointRemovable(breakpoint))
987                 WI.debuggerManager.removeBreakpoint(breakpoint);
988         }
989     }
990
991     _toggleAllBreakpoints(breakpoints, disabled)
992     {
993         for (var i = 0; i < breakpoints.length; ++i)
994             breakpoints[i].disabled = disabled;
995     }
996
997     _breakpointTreeOutlineDeleteTreeElement(selectedTreeElement)
998     {
999         console.assert(selectedTreeElement.selected);
1000
1001         if (!WI.debuggerManager.isBreakpointRemovable(selectedTreeElement.representedObject)) {
1002             let treeElementToSelect = selectedTreeElement.nextSelectableSibling;
1003             if (treeElementToSelect) {
1004                 const omitFocus = true;
1005                 const selectedByUser = true;
1006                 treeElementToSelect.select(omitFocus, selectedByUser);
1007             }
1008         } else if (selectedTreeElement instanceof WI.ResourceTreeElement || selectedTreeElement instanceof WI.ScriptTreeElement) {
1009             let breakpoints = this._breakpointsBeneathTreeElement(selectedTreeElement);
1010             this._removeAllBreakpoints(breakpoints);
1011         } else if (selectedTreeElement.representedObject === DebuggerSidebarPanel.__windowEventTargetRepresentedObject) {
1012             let eventBreakpointsOnWindow = WI.domManager.eventListenerBreakpoints.filter((eventBreakpoint) => eventBreakpoint.eventListener.onWindow);
1013             for (let eventBreakpoint of eventBreakpointsOnWindow)
1014                 WI.domManager.removeBreakpointForEventListener(eventBreakpoint.eventListener);
1015         }
1016
1017         return true;
1018     }
1019
1020     _breakpointTreeOutlineContextMenuTreeElement(contextMenu, event, treeElement)
1021     {
1022         // This check is necessary since the context menu is created by the TreeOutline, meaning
1023         // that any child could be the target of the context menu event.
1024         if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
1025             return;
1026
1027         let breakpoints = this._breakpointsBeneathTreeElement(treeElement);
1028         let shouldDisable = breakpoints.some((breakpoint) => !breakpoint.disabled);
1029
1030         let removeAllResourceBreakpoints = () => {
1031             this._removeAllBreakpoints(breakpoints);
1032         };
1033
1034         let toggleAllResourceBreakpoints = () => {
1035             this._toggleAllBreakpoints(breakpoints, shouldDisable);
1036         };
1037
1038         if (shouldDisable)
1039             contextMenu.appendItem(WI.UIString("Disable Breakpoints"), toggleAllResourceBreakpoints);
1040         else
1041             contextMenu.appendItem(WI.UIString("Enable Breakpoints"), toggleAllResourceBreakpoints);
1042         contextMenu.appendItem(WI.UIString("Delete Breakpoints"), removeAllResourceBreakpoints);
1043     }
1044
1045     _treeSelectionDidChange(event)
1046     {
1047         if (!this.selected)
1048             return;
1049
1050         let treeElement = event.target.selectedTreeElement;
1051         if (!treeElement)
1052             return;
1053
1054         if (treeElement instanceof WI.DOMNodeTreeElement
1055             || treeElement instanceof WI.DOMBreakpointTreeElement
1056             || treeElement instanceof WI.EventBreakpointTreeElement
1057             || treeElement instanceof WI.URLBreakpointTreeElement)
1058             return;
1059
1060         if (treeElement.representedObject === DebuggerSidebarPanel.__windowEventTargetRepresentedObject)
1061             return;
1062
1063         const options = {
1064             ignoreNetworkTab: true,
1065             ignoreSearchTab: true,
1066         };
1067
1068         if (treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement) {
1069             WI.showSourceCode(treeElement.representedObject, options);
1070             return;
1071         }
1072
1073         if (treeElement instanceof WI.CallFrameTreeElement) {
1074             let callFrame = treeElement.callFrame;
1075             if (callFrame.id)
1076                 WI.debuggerManager.activeCallFrame = callFrame;
1077
1078             if (callFrame.sourceCodeLocation)
1079                 WI.showSourceCodeLocation(callFrame.sourceCodeLocation, options);
1080
1081             return;
1082         }
1083
1084         if (treeElement instanceof WI.IssueTreeElement) {
1085             WI.showSourceCodeLocation(treeElement.issueMessage.sourceCodeLocation, options);
1086             return;
1087         }
1088
1089         if (!(treeElement instanceof WI.BreakpointTreeElement))
1090             return;
1091
1092         let breakpoint = treeElement.breakpoint;
1093         if (treeElement.treeOutline === this._pauseReasonTreeOutline) {
1094             WI.showSourceCodeLocation(breakpoint.sourceCodeLocation, options);
1095             return;
1096         }
1097
1098         if (!treeElement.parent.representedObject)
1099             return;
1100
1101         console.assert(treeElement.parent.representedObject instanceof WI.SourceCode);
1102         if (!(treeElement.parent.representedObject instanceof WI.SourceCode))
1103             return;
1104
1105         WI.showSourceCodeLocation(breakpoint.sourceCodeLocation, options);
1106     }
1107
1108     _addTreeElement(treeElement, parentTreeElement)
1109     {
1110         if (!parentTreeElement)
1111             parentTreeElement = this._breakpointsContentTreeOutline;
1112
1113         let comparator = (a, b) => {
1114             const rankFunctions = [
1115                 (treeElement) => treeElement.representedObject === WI.debuggerManager.allExceptionsBreakpoint,
1116                 (treeElement) => treeElement.representedObject === WI.debuggerManager.uncaughtExceptionsBreakpoint,
1117                 (treeElement) => treeElement.representedObject === WI.debuggerManager.assertionFailuresBreakpoint,
1118                 (treeElement) => treeElement instanceof WI.BreakpointTreeElement || treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement,
1119                 (treeElement) => treeElement.representedObject === WI.debuggerManager.allMicrotasksBreakpoint,
1120                 (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allAnimationFramesBreakpoint,
1121                 (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allTimeoutsBreakpoint,
1122                 (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allIntervalsBreakpoint,
1123                 (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allListenersBreakpoint,
1124                 (treeElement) => treeElement instanceof WI.EventBreakpointTreeElement,
1125                 (treeElement) => treeElement instanceof WI.DOMNodeTreeElement,
1126                 (treeElement) => treeElement.representedObject === DebuggerSidebarPanel.__windowEventTargetRepresentedObject,
1127                 (treeElement) => treeElement instanceof WI.DOMBreakpointTreeElement,
1128                 (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allRequestsBreakpoint,
1129                 (treeElement) => treeElement instanceof WI.URLBreakpointTreeElement,
1130             ];
1131
1132             let aRank = rankFunctions.findIndex((rankFunction) => rankFunction(a));
1133             let bRank = rankFunctions.findIndex((rankFunction) => rankFunction(b));
1134             if (aRank >= 0 && bRank >= 0) {
1135                 if (aRank < bRank)
1136                     return -1;
1137                 if (bRank < aRank)
1138                     return 1;
1139             }
1140
1141             if (a instanceof WI.BreakpointTreeElement && b instanceof WI.BreakpointTreeElement)
1142                 return this._compareBreakpointTreeElements(a, b);
1143
1144             return a.mainTitle.extendedLocaleCompare(b.mainTitle) || a.subtitle.extendedLocaleCompare(b.subtitle);
1145         };
1146
1147         parentTreeElement.insertChild(treeElement, insertionIndexForObjectInListSortedByFunction(treeElement, parentTreeElement.children, comparator));
1148     }
1149
1150     _compareBreakpointTreeElements(a, b)
1151     {
1152         if (!a.representedObject || !b.representedObject)
1153             return 0;
1154
1155         let aLocation = a.representedObject.sourceCodeLocation;
1156         let bLocation = b.representedObject.sourceCodeLocation;
1157         if (!aLocation || !bLocation)
1158             return 0;
1159
1160         var comparisonResult = aLocation.displayLineNumber - bLocation.displayLineNumber;
1161         if (comparisonResult !== 0)
1162             return comparisonResult;
1163
1164         return aLocation.displayColumnNumber - bLocation.displayColumnNumber;
1165     }
1166
1167     _updatePauseReason()
1168     {
1169         this._pauseReasonTreeOutline = null;
1170
1171         this._updatePauseReasonGotoArrow();
1172         let target = WI.debuggerManager.activeCallFrame.target;
1173         let targetData = WI.debuggerManager.dataForTarget(target);
1174         return this._updatePauseReasonSection(target, targetData.pauseReason, targetData.pauseData);
1175     }
1176
1177     _updatePauseReasonSection(target, pauseReason, pauseData)
1178     {
1179         switch (pauseReason) {
1180         case WI.DebuggerManager.PauseReason.AnimationFrame:
1181             this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1182
1183             var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(WI.domDebuggerManager.allAnimationFramesBreakpoint, {
1184                 className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
1185                 title: WI.UIString("requestAnimationFrame Fired"),
1186             });
1187             this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
1188
1189             var eventBreakpointRow = new WI.DetailsSectionRow;
1190             eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1191
1192             this._pauseReasonGroup.rows = [eventBreakpointRow];
1193             return true;
1194
1195         case WI.DebuggerManager.PauseReason.Assertion:
1196             // FIXME: We should include the assertion condition string.
1197             console.assert(pauseData, "Expected data with an assertion, but found none.");
1198             if (pauseData && pauseData.message) {
1199                 this._pauseReasonTextRow.text = WI.UIString("Assertion with message: %s").format(pauseData.message);
1200                 return true;
1201             }
1202
1203             this._pauseReasonTextRow.text = WI.UIString("Assertion Failed");
1204             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1205             return true;
1206
1207         case WI.DebuggerManager.PauseReason.BlackboxedScript: {
1208             console.assert(pauseData);
1209             if (pauseData)
1210                 this._updatePauseReasonSection(target, WI.DebuggerManager.pauseReasonFromPayload(pauseData.originalReason), pauseData.originalData);
1211
1212             // Don't use `_pauseReasonTextRow` as it may have already been set.
1213             let blackboxReasonTextRow = new WI.DetailsSectionTextRow(WI.UIString("Deferred pause from blackboxed script"));
1214             blackboxReasonTextRow.__blackboxReason = true;
1215
1216             let existingRows = this._pauseReasonGroup.rows.filter((row) => !row.__blackboxReason);
1217             this._pauseReasonGroup.rows = [blackboxReasonTextRow, ...existingRows];
1218             return true;
1219         }
1220
1221         case WI.DebuggerManager.PauseReason.Breakpoint:
1222             console.assert(pauseData, "Expected breakpoint identifier, but found none.");
1223             if (pauseData && pauseData.breakpointId) {
1224                 this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1225                 this._pauseReasonTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
1226
1227                 let breakpoint = WI.debuggerManager.breakpointForIdentifier(pauseData.breakpointId);
1228                 let breakpointTreeElement = new WI.BreakpointTreeElement(breakpoint, {
1229                     className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
1230                     title: WI.UIString("Triggered Breakpoint"),
1231                 });
1232                 let breakpointDetailsSection = new WI.DetailsSectionRow;
1233                 this._pauseReasonTreeOutline.appendChild(breakpointTreeElement);
1234                 breakpointDetailsSection.element.appendChild(this._pauseReasonTreeOutline.element);
1235
1236                 this._pauseReasonGroup.rows = [breakpointDetailsSection];
1237                 return true;
1238             }
1239             break;
1240
1241         case WI.DebuggerManager.PauseReason.CSPViolation:
1242             console.assert(pauseData, "Expected data with a CSP Violation, but found none.");
1243             if (pauseData) {
1244                 // COMPATIBILITY (iOS 8): 'directive' was 'directiveText'.
1245                 this._pauseReasonTextRow.text = WI.UIString("Content Security Policy violation of directive: %s").format(pauseData.directive || pauseData.directiveText);
1246                 this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1247                 return true;
1248             }
1249             break;
1250
1251         case WI.DebuggerManager.PauseReason.DebuggerStatement:
1252             this._pauseReasonTextRow.text = WI.UIString("Debugger Statement");
1253             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1254             return true;
1255
1256         case WI.DebuggerManager.PauseReason.DOM:
1257             console.assert(WI.domDebuggerManager.supported);
1258             console.assert(pauseData, "Expected DOM breakpoint data, but found none.");
1259             if (pauseData && pauseData.nodeId) {
1260                 let domNode = WI.domManager.nodeForId(pauseData.nodeId);
1261                 let domBreakpoints = WI.domDebuggerManager.domBreakpointsForNode(domNode);
1262                 let domBreakpoint;
1263                 for (let breakpoint of domBreakpoints) {
1264                     if (breakpoint.type === pauseData.type) {
1265                         domBreakpoint = breakpoint;
1266                         break;
1267                     }
1268                 }
1269
1270                 if (!domBreakpoint)
1271                     return;
1272
1273                 this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1274
1275                 let type = WI.DOMBreakpointTreeElement.displayNameForType(domBreakpoint.type);
1276                 let domBreakpointTreeElement = new WI.DOMBreakpointTreeElement(domBreakpoint, {
1277                     className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
1278                     title: type,
1279                 });
1280                 let domBreakpointRow = new WI.DetailsSectionRow;
1281                 this._pauseReasonTreeOutline.appendChild(domBreakpointTreeElement);
1282                 domBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1283
1284                 let ownerElementRow = new WI.DetailsSectionSimpleRow(WI.UIString("Element"), WI.linkifyNodeReference(domNode));
1285                 this._pauseReasonGroup.rows = [domBreakpointRow, ownerElementRow];
1286
1287                 if (domBreakpoint.type !== WI.DOMBreakpoint.Type.SubtreeModified)
1288                     return true;
1289
1290                 console.assert(pauseData.targetNode);
1291
1292                 let remoteObject = WI.RemoteObject.fromPayload(pauseData.targetNode, target);
1293                 remoteObject.pushNodeToFrontend((nodeId) => {
1294                     if (!nodeId)
1295                         return;
1296
1297                     let node = WI.domManager.nodeForId(nodeId);
1298                     console.assert(node, "Missing node for id.", nodeId);
1299                     if (!node)
1300                         return;
1301
1302                     let fragment = document.createDocumentFragment();
1303                     let description = pauseData.insertion ? WI.UIString("Child added to ") : WI.UIString("Removed descendant ");
1304                     fragment.append(description, WI.linkifyNodeReference(node));
1305
1306                     let targetDescriptionRow = new WI.DetailsSectionSimpleRow(WI.UIString("Details"), fragment);
1307                     targetDescriptionRow.element.classList.add("target-description");
1308
1309                     this._pauseReasonGroup.rows = this._pauseReasonGroup.rows.concat(targetDescriptionRow);
1310                 });
1311
1312                 return true;
1313             }
1314             break;
1315
1316         case WI.DebuggerManager.PauseReason.Listener:
1317         case WI.DebuggerManager.PauseReason.EventListener:
1318             console.assert(pauseData, "Expected data with an event listener, but found none.");
1319             if (!pauseData)
1320                 return false;
1321
1322             var eventBreakpoint = null;
1323             if (pauseData.eventListenerId)
1324                 eventBreakpoint = WI.domManager.breakpointForEventListenerId(pauseData.eventListenerId);
1325             if (!eventBreakpoint)
1326                 eventBreakpoint = WI.domDebuggerManager.listenerBreakpointForEventName(pauseData.eventName);
1327
1328             console.assert(eventBreakpoint, "Expected Event Listener breakpoint for event name.", pauseData.eventName);
1329             if (!eventBreakpoint)
1330                 return false;
1331
1332             this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1333
1334             var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, {
1335                 className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
1336                 title: WI.UIString("\u201C%s\u201D Event Fired").format(pauseData.eventName),
1337             });
1338             this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
1339
1340             var eventBreakpointRow = new WI.DetailsSectionRow;
1341             eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1342
1343             var rows = [eventBreakpointRow];
1344
1345             var eventListener = eventBreakpoint.eventListener;
1346             if (eventListener) {
1347                 console.assert(eventListener.eventListenerId === pauseData.eventListenerId);
1348
1349                 let value = null;
1350                 if (eventListener.onWindow)
1351                     value = WI.unlocalizedString("window");
1352                 else if (eventListener.node)
1353                     value = WI.linkifyNodeReference(eventListener.node);
1354                 if (value)
1355                     rows.push(new WI.DetailsSectionSimpleRow(WI.UIString("Target"), value));
1356             }
1357
1358             this._pauseReasonGroup.rows = rows;
1359             return true;
1360
1361         case WI.DebuggerManager.PauseReason.Exception:
1362             console.assert(pauseData, "Expected data with an exception, but found none.");
1363             if (pauseData) {
1364                 // FIXME: We should improve the appearance of thrown objects. This works well for exception strings.
1365                 var data = WI.RemoteObject.fromPayload(pauseData, target);
1366                 this._pauseReasonTextRow.text = WI.UIString("Exception with thrown value: %s").format(data.description);
1367                 this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1368                 return true;
1369             }
1370             break;
1371
1372         case WI.DebuggerManager.PauseReason.Interval:
1373             this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1374
1375             var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(WI.domDebuggerManager.allIntervalsBreakpoint, {
1376                 className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
1377                 title: WI.UIString("setInterval Fired"),
1378             });
1379             this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
1380
1381             var eventBreakpointRow = new WI.DetailsSectionRow;
1382             eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1383
1384             this._pauseReasonGroup.rows = [eventBreakpointRow];
1385             return true;
1386
1387         case WI.DebuggerManager.PauseReason.Microtask:
1388             this._pauseReasonTextRow.text = WI.UIString("Microtask Fired");
1389             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1390             return true;
1391
1392         case WI.DebuggerManager.PauseReason.PauseOnNextStatement:
1393             this._pauseReasonTextRow.text = WI.UIString("Immediate Pause Requested");
1394             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1395             return true;
1396
1397         case WI.DebuggerManager.PauseReason.Timer:
1398             console.assert(pauseData, "Expected data with a timer, but found none.");
1399             if (!pauseData)
1400                 return false;
1401
1402             var eventBreakpoint = null;
1403             switch (pauseData.eventName) {
1404             case "setTimeout":
1405                 eventBreakpoint = WI.domDebuggerManager.allTimeoutsBreakpoint;
1406                 break;
1407
1408             case "setInterval":
1409                 eventBreakpoint = WI.domDebuggerManager.allIntervalsBreakpoint;
1410                 break;
1411             }
1412             console.assert(eventBreakpoint, "Expected Timer breakpoint for event name.", pauseData.eventName);
1413             if (!eventBreakpoint)
1414                 return false;
1415
1416             this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1417
1418             var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, {
1419                 className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
1420                 title: WI.UIString("%s Fired").format(pauseData.eventName),
1421             });
1422             this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
1423
1424             var eventBreakpointRow = new WI.DetailsSectionRow;
1425             eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1426
1427             this._pauseReasonGroup.rows = [eventBreakpointRow];
1428             return true;
1429
1430         case WI.DebuggerManager.PauseReason.Timeout:
1431             this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1432
1433             var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(WI.domDebuggerManager.allTimeoutsBreakpoint, {
1434                 className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
1435                 title: WI.UIString("setTimeout Fired"),
1436             });
1437             this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
1438
1439             var eventBreakpointRow = new WI.DetailsSectionRow;
1440             eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1441
1442             this._pauseReasonGroup.rows = [eventBreakpointRow];
1443             return true;
1444
1445         case WI.DebuggerManager.PauseReason.Fetch:
1446         case WI.DebuggerManager.PauseReason.XHR:
1447             console.assert(WI.domDebuggerManager.supported);
1448             console.assert(pauseData, "Expected URL breakpoint data, but found none.");
1449             if (pauseData) {
1450                 if (pauseData.breakpointURL) {
1451                     let urlBreakpoint = WI.domDebuggerManager.urlBreakpointForURL(pauseData.breakpointURL);
1452                     console.assert(urlBreakpoint, "Expected URL breakpoint for URL.", pauseData.breakpointURL);
1453
1454                     this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1455
1456                     let urlBreakpointTreeElement = new WI.URLBreakpointTreeElement(urlBreakpoint, {
1457                         className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
1458                         title: WI.DOMDebuggerManager.supportsURLBreakpoints() ? WI.UIString("Triggered URL Breakpoint") : WI.UIString("Triggered XHR Breakpoint"),
1459                     });
1460                     let urlBreakpointRow = new WI.DetailsSectionRow;
1461                     this._pauseReasonTreeOutline.appendChild(urlBreakpointTreeElement);
1462                     urlBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1463
1464                     this._pauseReasonTextRow.text = pauseData.url;
1465                     this._pauseReasonGroup.rows = [urlBreakpointRow, this._pauseReasonTextRow];
1466                 } else {
1467                     console.assert(pauseData.breakpointURL === "", "Should be the All Requests breakpoint which has an empty URL");
1468                     this._pauseReasonTextRow.text = WI.UIString("Requesting: %s").format(pauseData.url);
1469                     this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1470                 }
1471                 this._pauseReasonTextRow.element.title = pauseData.url;
1472                 return true;
1473             }
1474             break;
1475
1476         case WI.DebuggerManager.PauseReason.Other:
1477             console.error("Paused for unknown reason. We should always have a reason.");
1478             break;
1479         }
1480
1481         return false;
1482     }
1483
1484     _updatePauseReasonGotoArrow()
1485     {
1486         this._pauseReasonLinkContainerElement.removeChildren();
1487
1488         var activeCallFrame = WI.debuggerManager.activeCallFrame;
1489         if (!activeCallFrame)
1490             return;
1491
1492         var sourceCodeLocation = activeCallFrame.sourceCodeLocation;
1493         if (!sourceCodeLocation)
1494             return;
1495
1496         const options = {
1497             useGoToArrowButton: true,
1498             ignoreNetworkTab: true,
1499             ignoreSearchTab: true,
1500         };
1501         let linkElement = WI.createSourceCodeLocationLink(sourceCodeLocation, options);
1502         this._pauseReasonLinkContainerElement.appendChild(linkElement);
1503     }
1504
1505     _addIssue(issueMessage, sourceCode)
1506     {
1507         let issueTreeElement = this._scriptsContentTreeOutline.findTreeElement(issueMessage);
1508         if (issueTreeElement)
1509             return issueTreeElement;
1510
1511         console.assert(sourceCode || (issueMessage.sourceCodeLocation && issueMessage.sourceCodeLocation.sourceCode));
1512         let parentTreeElement = this._addTreeElementForSourceCodeToTreeOutline(sourceCode || issueMessage.sourceCodeLocation.sourceCode, this._scriptsContentTreeOutline);
1513         if (!parentTreeElement)
1514             return null;
1515
1516         issueTreeElement = new WI.IssueTreeElement(issueMessage);
1517
1518         parentTreeElement.insertChild(issueTreeElement, insertionIndexForObjectInListSortedByFunction(issueTreeElement, parentTreeElement.children, this._compareBreakpointTreeElements));
1519         if (parentTreeElement.children.length === 1)
1520             parentTreeElement.expand();
1521
1522         return issueTreeElement;
1523     }
1524
1525     _handleDOMBreakpointDOMNodeChanged(event)
1526     {
1527         let breakpoint = event.target;
1528         if (breakpoint.domNodeIdentifier)
1529             this._addBreakpoint(breakpoint);
1530         else
1531             this._removeBreakpoint(breakpoint);
1532     }
1533
1534     _handleIssueAdded(event)
1535     {
1536         var issue = event.data.issue;
1537
1538         // We only want to show issues originating from JavaScript source code.
1539         if (!issue.sourceCodeLocation || !issue.sourceCodeLocation.sourceCode || (issue.source !== "javascript" && issue.source !== "console-api"))
1540             return;
1541
1542         this._addIssue(issue);
1543     }
1544
1545     _handleIssuesCleared(event)
1546     {
1547         let currentTreeElement = this._scriptsContentTreeOutline.children[0];
1548         let issueTreeElements = [];
1549
1550         while (currentTreeElement && !currentTreeElement.root) {
1551             if (currentTreeElement instanceof WI.IssueTreeElement)
1552                 issueTreeElements.push(currentTreeElement);
1553             currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true);
1554         }
1555
1556         issueTreeElements.forEach((treeElement) => treeElement.parent.removeChild(treeElement));
1557     }
1558
1559     _handleBreakpointElementAddedOrRemoved(event)
1560     {
1561         let treeElement = event.data.element;
1562
1563         let setting = null;
1564         switch (treeElement.representedObject) {
1565         case WI.debuggerManager.assertionFailuresBreakpoint:
1566             setting = WI.settings.showAssertionFailuresBreakpoint;
1567             break;
1568
1569         case WI.debuggerManager.allMicrotasksBreakpoint:
1570             setting = WI.settings.showAllMicrotasksBreakpoint;
1571             break;
1572
1573         case WI.domDebuggerManager.allAnimationFramesBreakpoint:
1574             setting = WI.settings.showAllAnimationFramesBreakpoint;
1575             break;
1576
1577         case WI.domDebuggerManager.allIntervalsBreakpoint:
1578             setting = WI.settings.showAllIntervalsBreakpoint;
1579             break;
1580
1581         case WI.domDebuggerManager.allListenersBreakpoint:
1582             setting = WI.settings.showAllListenersBreakpoint;
1583             break;
1584
1585         case WI.domDebuggerManager.allRequestsBreakpoint:
1586             setting = WI.settings.showAllRequestsBreakpoint;
1587             break;
1588
1589         case WI.domDebuggerManager.allTimeoutsBreakpoint:
1590             setting = WI.settings.showAllTimeoutsBreakpoint;
1591             break;
1592         }
1593         if (setting)
1594             setting.value = !!treeElement.parent;
1595
1596         if (event.type === WI.TreeOutline.Event.ElementRemoved) {
1597             let selectedTreeElement = this._breakpointsContentTreeOutline.selectedTreeElement;
1598             if (selectedTreeElement) {
1599                 if (selectedTreeElement.representedObject === WI.debuggerManager.assertionFailuresBreakpoint || !WI.debuggerManager.isBreakpointRemovable(selectedTreeElement.representedObject)) {
1600                     const skipUnrevealed = true;
1601                     const dontPopulate = true;
1602                     let treeElementToSelect = selectedTreeElement.traverseNextTreeElement(skipUnrevealed, dontPopulate);
1603                     if (treeElementToSelect) {
1604                         const omitFocus = true;
1605                         const selectedByUser = true;
1606                         treeElementToSelect.select(omitFocus, selectedByUser);
1607                     }
1608                 }
1609             }
1610         }
1611     }
1612
1613     _populateCreateBreakpointContextMenu(contextMenu)
1614     {
1615         // COMPATIBILITY (iOS 10): DebuggerAgent.setPauseOnAssertions did not exist yet.
1616         if (InspectorBackend.domains.Debugger.setPauseOnAssertions) {
1617             let assertionFailuresBreakpointShown = WI.settings.showAssertionFailuresBreakpoint.value;
1618
1619             contextMenu.appendCheckboxItem(WI.repeatedUIString.assertionFailures(), () => {
1620                 if (assertionFailuresBreakpointShown)
1621                     WI.debuggerManager.removeBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint);
1622                 else {
1623                     WI.debuggerManager.assertionFailuresBreakpoint.disabled = false;
1624                     WI.debuggerManager.addBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint);
1625                 }
1626             }, assertionFailuresBreakpointShown);
1627         }
1628
1629         contextMenu.appendSeparator();
1630
1631         // COMPATIBILITY (iOS 13): DebuggerAgent.setPauseOnMicrotasks did not exist yet.
1632         if (InspectorBackend.domains.Debugger.setPauseOnMicrotasks) {
1633             let allMicrotasksBreakpointShown = WI.settings.showAllMicrotasksBreakpoint.value;
1634
1635             contextMenu.appendCheckboxItem(WI.UIString("All Microtasks"), () => {
1636                 if (allMicrotasksBreakpointShown)
1637                     WI.debuggerManager.removeBreakpoint(WI.debuggerManager.allMicrotasksBreakpoint);
1638                 else {
1639                     WI.debuggerManager.allMicrotasksBreakpoint.disabled = false;
1640                     WI.debuggerManager.addBreakpoint(WI.debuggerManager.allMicrotasksBreakpoint);
1641                 }
1642             }, allMicrotasksBreakpointShown);
1643         }
1644
1645         if (WI.DOMDebuggerManager.supportsEventBreakpoints() || WI.DOMDebuggerManager.supportsEventListenerBreakpoints()) {
1646             function addToggleForSpecialEventBreakpoint(breakpoint, label, checked) {
1647                 contextMenu.appendCheckboxItem(label, () => {
1648                     if (checked)
1649                         WI.domDebuggerManager.removeEventBreakpoint(breakpoint);
1650                     else {
1651                         breakpoint.disabled = false;
1652                         WI.domDebuggerManager.addEventBreakpoint(breakpoint);
1653                     }
1654                 }, checked);
1655             }
1656
1657             addToggleForSpecialEventBreakpoint(WI.domDebuggerManager.allAnimationFramesBreakpoint, WI.UIString("All Animation Frames"), WI.settings.showAllAnimationFramesBreakpoint.value);
1658             addToggleForSpecialEventBreakpoint(WI.domDebuggerManager.allTimeoutsBreakpoint, WI.UIString("All Timeouts"), WI.settings.showAllTimeoutsBreakpoint.value);
1659             addToggleForSpecialEventBreakpoint(WI.domDebuggerManager.allIntervalsBreakpoint, WI.UIString("All Intervals"), WI.settings.showAllIntervalsBreakpoint.value);
1660
1661             contextMenu.appendSeparator();
1662
1663             if (WI.DOMDebuggerManager.supportsAllListenersBreakpoint())
1664                 addToggleForSpecialEventBreakpoint(WI.domDebuggerManager.allListenersBreakpoint, WI.UIString("All Events"), WI.settings.showAllListenersBreakpoint.value);
1665
1666             contextMenu.appendItem(WI.UIString("Event Breakpoint\u2026"), () => {
1667                 let popover = new WI.EventBreakpointPopover(this);
1668                 popover.show(this._createBreakpointButton.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]);
1669             });
1670         }
1671
1672         if (WI.DOMDebuggerManager.supportsURLBreakpoints() || WI.DOMDebuggerManager.supportsXHRBreakpoints()) {
1673             contextMenu.appendSeparator();
1674
1675             let allRequestsBreakpointShown = WI.settings.showAllRequestsBreakpoint.value;
1676             contextMenu.appendCheckboxItem(WI.repeatedUIString.allRequests(), () => {
1677                 if (allRequestsBreakpointShown)
1678                     WI.domDebuggerManager.removeURLBreakpoint(WI.domDebuggerManager.allRequestsBreakpoint);
1679                 else {
1680                     WI.domDebuggerManager.allRequestsBreakpoint.disabled = false;
1681                     WI.domDebuggerManager.addURLBreakpoint(WI.domDebuggerManager.allRequestsBreakpoint);
1682                 }
1683             }, allRequestsBreakpointShown);
1684
1685             contextMenu.appendItem(WI.DOMDebuggerManager.supportsURLBreakpoints() ? WI.UIString("URL Breakpoint\u2026") : WI.UIString("XHR Breakpoint\u2026"), () => {
1686                 let popover = new WI.URLBreakpointPopover(this);
1687                 popover.show(this._createBreakpointButton.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]);
1688             });
1689         }
1690     }
1691 };
1692
1693 WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName = "paused";
1694 WI.DebuggerSidebarPanel.ExceptionIconStyleClassName = "breakpoint-exception-icon";
1695 WI.DebuggerSidebarPanel.AssertionIconStyleClassName = "breakpoint-assertion-icon";
1696 WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName = "breakpoint-paused-icon";
1697
1698 WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey = "debugger-sidebar-panel-all-exceptions-breakpoint";
1699 WI.DebuggerSidebarPanel.SelectedUncaughtExceptionsCookieKey = "debugger-sidebar-panel-uncaught-exceptions-breakpoint";
1700 WI.DebuggerSidebarPanel.SelectedAssertionFailuresCookieKey = "debugger-sidebar-panel-assertion-failures-breakpoint";
1701 WI.DebuggerSidebarPanel.SelectedAllMicrotasksCookieKey = "debugger-sidebar-panel-all-microtasks-breakpoint";
1702 WI.DebuggerSidebarPanel.SelectedAllAnimationFramesBreakpoint = "debugger-sidebar-panel-all-animation-frames-breakpoint";
1703 WI.DebuggerSidebarPanel.SelectedAllIntervalsBreakpoint = "debugger-sidebar-panel-all-intervals-breakpoint";
1704 WI.DebuggerSidebarPanel.SelectedAllListenersBreakpoint = "debugger-sidebar-panel-all-listeners-breakpoint";
1705 WI.DebuggerSidebarPanel.SelectedAllTimeoutsBreakpoint = "debugger-sidebar-panel-all-timeouts-breakpoint";
1706 WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey = "debugger-sidebar-panel-all-requests-breakpoint";