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