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