Web Inspector: Sources: provide option to group by path
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / SourcesNavigationSidebarPanel.js
1 /*
2  * Copyright (C) 2018 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.SourcesNavigationSidebarPanel = class SourcesNavigationSidebarPanel extends WI.NavigationSidebarPanel
27 {
28     constructor()
29     {
30         const shouldAutoPruneStaleTopLevelResourceTreeElements = true;
31         super("sources", WI.UIString("Sources"), shouldAutoPruneStaleTopLevelResourceTreeElements);
32
33         this._workerTargetTreeElementMap = new Map;
34         this._mainFrameTreeElement = null;
35         this._extensionScriptsFolderTreeElement = null;
36         this._extraScriptsFolderTreeElement = null;
37         this._anonymousScriptsFolderTreeElement = null;
38
39         this._originTreeElementMap = new Map;
40
41         this._boundCompareTreeElements = this._compareTreeElements.bind(this);
42
43         this._debuggerNavigationBar = new WI.NavigationBar;
44         this.addSubview(this._debuggerNavigationBar);
45
46         let createActivateNavigationItem = ({identifier, defaultToolTip, activatedToolTip, image}) => {
47             let navigationItem = new WI.ActivateButtonNavigationItem(identifier, defaultToolTip, activatedToolTip, image, 15, 15);
48             this._debuggerNavigationBar.addNavigationItem(navigationItem);
49             return navigationItem;
50         };
51
52         let createToggleNavigationItem = ({identifier, defaultToolTip, alternateToolTip, defaultImage, alternateImage}) => {
53             let navigationItem = new WI.ToggleButtonNavigationItem(identifier, defaultToolTip, alternateToolTip, defaultImage, alternateImage, 15, 15);
54             this._debuggerNavigationBar.addNavigationItem(navigationItem);
55             return navigationItem;
56         };
57
58         let createButtonNavigationitem = ({identifier, toolTipOrLabel, image}) => {
59             let navigationItem = new WI.ButtonNavigationItem(identifier, toolTipOrLabel, image, 15, 15);
60             this._debuggerNavigationBar.addNavigationItem(navigationItem);
61             return navigationItem;
62         };
63
64         this._debuggerBreakpointsButtonItem = createActivateNavigationItem({
65             identifier: "debugger-breakpoints",
66             defaultToolTip: WI.UIString("Enable all breakpoints (%s)").format(WI.toggleBreakpointsKeyboardShortcut.displayName),
67             activatedToolTip: WI.UIString("Disable all breakpoints (%s)").format(WI.toggleBreakpointsKeyboardShortcut.displayName),
68             image: "Images/Breakpoints.svg",
69         });
70         this._debuggerBreakpointsButtonItem.activated = WI.debuggerManager.breakpointsEnabled;
71         this._debuggerBreakpointsButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerToggleBreakpoints, this);
72
73         this._debuggerPauseResumeButtonItem = createToggleNavigationItem({
74             identifier: "debugger-pause-resume",
75             defaultToolTip: WI.UIString("Pause script execution (%s or %s)").format(WI.pauseOrResumeKeyboardShortcut.displayName, WI.pauseOrResumeAlternateKeyboardShortcut.displayName),
76             alternateToolTip: WI.UIString("Continue script execution (%s or %s)").format(WI.pauseOrResumeKeyboardShortcut.displayName, WI.pauseOrResumeAlternateKeyboardShortcut.displayName),
77             defaultImage: "Images/Pause.svg",
78             alternateImage: "Images/Resume.svg",
79         });
80         this._debuggerPauseResumeButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerPauseResumeToggle, this);
81
82         this._debuggerStepOverButtonItem = createButtonNavigationitem({
83             identifier: "debugger-step-over",
84             toolTipOrLabel: WI.UIString("Step over (%s or %s)").format(WI.stepOverKeyboardShortcut.displayName, WI.stepOverAlternateKeyboardShortcut.displayName),
85             image: "Images/StepOver.svg",
86         });
87         this._debuggerStepOverButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepOver, this);
88         this._debuggerStepOverButtonItem.enabled = false;
89
90         this._debuggerStepIntoButtonItem = createButtonNavigationitem({
91             identifier: "debugger-step-into",
92             toolTipOrLabel: WI.UIString("Step into (%s or %s)").format(WI.stepIntoKeyboardShortcut.displayName, WI.stepIntoAlternateKeyboardShortcut.displayName),
93             image: "Images/StepInto.svg",
94         });
95         this._debuggerStepIntoButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepInto, this);
96         this._debuggerStepIntoButtonItem.enabled = false;
97
98         this._debuggerStepOutButtonItem = createButtonNavigationitem({
99             identifier: "debugger-step-out",
100             toolTipOrLabel: WI.UIString("Step out (%s or %s)").format(WI.stepOutKeyboardShortcut.displayName, WI.stepOutAlternateKeyboardShortcut.displayName),
101             image: "Images/StepOut.svg",
102         });
103         this._debuggerStepOutButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepOut, this);
104         this._debuggerStepOutButtonItem.enabled = false;
105
106         this._timelineRecordingWarningElement = null;
107         this._auditTestWarningElement = null;
108         this._breakpointsDisabledWarningElement = null;
109
110         this._pauseReasonTreeOutline = null;
111         this._pauseReasonLinkContainerElement = document.createElement("span");
112         this._pauseReasonTextRow = new WI.DetailsSectionTextRow;
113         this._pauseReasonGroup = new WI.DetailsSectionGroup([this._pauseReasonTextRow]);
114         this._pauseReasonSection = new WI.DetailsSection("paused-reason", WI.UIString("Pause Reason"), [this._pauseReasonGroup], this._pauseReasonLinkContainerElement);
115
116         this._callStackTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
117         this._callStackTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._handleTreeSelectionDidChange, this);
118
119         let callStackRow = new WI.DetailsSectionRow;
120         callStackRow.element.appendChild(this._callStackTreeOutline.element);
121
122         let callStackGroup = new WI.DetailsSectionGroup([callStackRow]);
123         this._callStackSection = new WI.DetailsSection("call-stack", WI.UIString("Call Stack"), [callStackGroup]);
124
125         this._mainTargetTreeElement = null;
126         this._activeCallFrameTreeElement = null;
127
128         // Prevent the breakpoints list from being used as the source of selection restoration (e.g. on reload or navigation).
129         this._breakpointsTreeOutline = this.createContentTreeOutline({ignoreCookieRestoration: true});
130         this._breakpointsTreeOutline.addEventListener(WI.TreeOutline.Event.ElementAdded, this._handleBreakpointElementAddedOrRemoved, this);
131         this._breakpointsTreeOutline.addEventListener(WI.TreeOutline.Event.ElementRemoved, this._handleBreakpointElementAddedOrRemoved, this);
132         this._breakpointsTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._handleTreeSelectionDidChange, this);
133         this._breakpointsTreeOutline.ondelete = (treeElement) => {
134             console.assert(treeElement.selected);
135             console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement);
136             if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
137                 return false;
138
139             let wasTopResourceTreeElement = treeElement.previousSibling === this._assertionsBreakpointTreeElement || treeElement.previousSibling === this._allUncaughtExceptionsBreakpointTreeElement;
140             let nextSibling = treeElement.nextSibling;
141
142             let breakpoints = this._breakpointsBeneathTreeElement(treeElement);
143             this._removeAllBreakpoints(breakpoints);
144
145             if (wasTopResourceTreeElement && nextSibling) {
146                 const omitFocus = true;
147                 const selectedByUser = true;
148                 nextSibling.select(omitFocus, selectedByUser);
149             }
150
151             return true;
152         };
153         this._breakpointsTreeOutline.populateContextMenu = (contextMenu, event, treeElement) => {
154             // This check is necessary since the context menu is created by the TreeOutline, meaning
155             // that any child could be the target of the context menu event.
156             if (treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement) {
157                 let breakpoints = this._breakpointsBeneathTreeElement(treeElement);
158
159                 let shouldDisable = breakpoints.some((breakpoint) => !breakpoint.disabled);
160                 contextMenu.appendItem(shouldDisable ? WI.UIString("Disable Breakpoints") : WI.UIString("Enable Breakpoints"), () => {
161                     this._toggleAllBreakpoints(breakpoints, shouldDisable);
162                 });
163
164                 contextMenu.appendItem(WI.UIString("Delete Breakpoints"), () => {
165                     this._removeAllBreakpoints(breakpoints);
166                 });
167             }
168
169             WI.TreeOutline.prototype.populateContextMenu(contextMenu, event, treeElement);
170         };
171
172         let breakpointsRow = new WI.DetailsSectionRow;
173         breakpointsRow.element.appendChild(this._breakpointsTreeOutline.element);
174
175         let breakpointNavigationBarWrapper = document.createElement("div");
176
177         let breakpointNavigationBar = new WI.NavigationBar;
178         breakpointNavigationBarWrapper.appendChild(breakpointNavigationBar.element);
179
180         this._createBreakpointButton = new WI.ButtonNavigationItem("create-breakpoint", WI.UIString("Create Breakpoint"), "Images/Plus13.svg", 13, 13);
181         this._createBreakpointButton.element.addEventListener("mousedown", this._handleCreateBreakpointMouseDown.bind(this));
182         breakpointNavigationBar.addNavigationItem(this._createBreakpointButton);
183
184         let breakpointsGroup = new WI.DetailsSectionGroup([breakpointsRow]);
185         let breakpointsSection = new WI.DetailsSection("breakpoints", WI.UIString("Breakpoints"), [breakpointsGroup], breakpointNavigationBarWrapper);
186         this.contentView.element.insertBefore(breakpointsSection.element, this.contentView.element.firstChild);
187
188         this._resourcesNavigationBar = new WI.NavigationBar;
189         this.contentView.addSubview(this._resourcesNavigationBar);
190         this.contentView.element.insertBefore(this._resourcesNavigationBar.element, breakpointsSection.nextSibling);
191
192         this._resourcesNavigationBar.addNavigationItem(new WI.FlexibleSpaceNavigationItem);
193
194         const resourceTypeScopeItemPrefix = "sources-resource-type-";
195         let resourceTypeScopeBarItems = [];
196         resourceTypeScopeBarItems.push(new WI.ScopeBarItem(resourceTypeScopeItemPrefix + "all", WI.UIString("All Resources"), {exclusive: true}));
197         for (let value of Object.values(WI.Resource.Type)) {
198             let scopeBarItem = new WI.ScopeBarItem(resourceTypeScopeItemPrefix + value, WI.Resource.displayNameForType(value, true));
199             scopeBarItem[WI.SourcesNavigationSidebarPanel.ResourceTypeSymbol] = value;
200             resourceTypeScopeBarItems.push(scopeBarItem);
201         }
202
203         const shouldGroupNonExclusiveItems = true;
204         this._resourceTypeScopeBar = new WI.ScopeBar("sources-resource-type-scope-bar", resourceTypeScopeBarItems, resourceTypeScopeBarItems[0], shouldGroupNonExclusiveItems);
205         this._resourceTypeScopeBar.addEventListener(WI.ScopeBar.Event.SelectionChanged, this._handleResourceTypeScopeBarSelectionChanged, this);
206         this._resourcesNavigationBar.addNavigationItem(this._resourceTypeScopeBar);
207
208         this._resourcesNavigationBar.addNavigationItem(new WI.FlexibleSpaceNavigationItem);
209
210         let resourceGroupingModeNavigationItem = new WI.ButtonNavigationItem("grouping-mode", WI.UIString("Grouping Mode"), "Images/Gear.svg", 15, 15);
211         resourceGroupingModeNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
212         resourceGroupingModeNavigationItem.element.addEventListener("mousedown", this._handleResourceGroupingModeMouseDown.bind(this));
213         this._resourcesNavigationBar.addNavigationItem(resourceGroupingModeNavigationItem);
214
215         let resourcesContainer = this.contentView.element.insertBefore(document.createElement("div"), this._resourcesNavigationBar.element.nextSibling);
216         resourcesContainer.classList.add("resources");
217
218         this._resourcesTreeOutline = this.contentTreeOutline;
219         this._resourcesTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._handleTreeSelectionDidChange, this);
220         this._resourcesTreeOutline.includeSourceMapResourceChildren = true;
221         resourcesContainer.appendChild(this._resourcesTreeOutline.element);
222
223         let onlyShowResourcesWithIssuesFilterFunction = (treeElement) => {
224             if (treeElement.treeOutline !== this._resourcesTreeOutline)
225                 return true;
226
227             if (treeElement instanceof WI.IssueTreeElement)
228                 return true;
229
230             if (treeElement.hasChildren) {
231                 for (let child of treeElement.children) {
232                     if (child instanceof WI.IssueTreeElement)
233                         return true;
234                 }
235             }
236             return false;
237         };
238         const activatedByDefault = false;
239         this.filterBar.addFilterBarButton("sources-only-show-resources-with-issues", onlyShowResourcesWithIssuesFilterFunction, activatedByDefault, WI.UIString("Only show resources with issues"), WI.UIString("Show all resources"), "Images/Errors.svg", 15, 15);
240
241         WI.settings.resourceGroupingMode.addEventListener(WI.Setting.Event.Changed, this._handleResourceGroupingModeChanged, this);
242
243         WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._handleFrameMainResourceDidChange, this);
244         WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, this._handleResourceAdded, this);
245         WI.Target.addEventListener(WI.Target.Event.ResourceAdded, this._handleResourceAdded, this);
246
247         WI.networkManager.addEventListener(WI.NetworkManager.Event.MainFrameDidChange, this._handleMainFrameDidChange, this);
248
249         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointAdded, this._handleDebuggerBreakpointAdded, this);
250         WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.DOMBreakpointAdded, this._handleDebuggerBreakpointAdded, this);
251         WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.EventBreakpointAdded, this._handleDebuggerBreakpointAdded, this);
252         WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.URLBreakpointAdded, this._handleDebuggerBreakpointAdded, this);
253
254         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointRemoved, this._handleDebuggerBreakpointRemoved, this);
255         WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, this._handleDebuggerBreakpointRemoved, this);
256         WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.EventBreakpointRemoved, this._handleDebuggerBreakpointRemoved, this);
257         WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.URLBreakpointRemoved, this._handleDebuggerBreakpointRemoved, this);
258
259         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointsEnabledDidChange, this._handleDebuggerBreakpointsEnabledDidChange, this);
260         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptAdded, this._handleDebuggerScriptAdded, this);
261         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptRemoved, this._handleDebuggerScriptRemoved, this);
262         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptsCleared, this._handleDebuggerScriptsCleared, this);
263         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, this._handleDebuggerPaused, this);
264         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Resumed, this._handleDebuggerResumed, this);
265         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.CallFramesDidChange, this._handleDebuggerCallFramesDidChange, this);
266         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this._handleDebuggerActiveCallFrameDidChange, this);
267         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.WaitingToPause, this._handleDebuggerWaitingToPause, this);
268
269         WI.Breakpoint.addEventListener(WI.Breakpoint.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
270         WI.IssueMessage.addEventListener(WI.IssueMessage.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
271
272         WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeChanged, this._handleDOMBreakpointDOMNodeChanged, this);
273
274         WI.consoleManager.addEventListener(WI.ConsoleManager.Event.IssueAdded, this._handleConsoleIssueAdded, this);
275         WI.consoleManager.addEventListener(WI.ConsoleManager.Event.Cleared, this._handleConsoleCleared, this);
276
277         WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingWillStart, this._handleTimelineCapturingWillStart, this);
278         WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStopped, this._handleTimelineCapturingStopped, this);
279
280         WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditManagerTestScheduled, this);
281         WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditManagerTestCompleted, this);
282
283         WI.cssManager.addEventListener(WI.CSSManager.Event.StyleSheetAdded, this._handleCSSStyleSheetAdded, this);
284
285         WI.targetManager.addEventListener(WI.TargetManager.Event.TargetAdded, this._handleTargetAdded, this);
286         WI.targetManager.addEventListener(WI.TargetManager.Event.TargetRemoved, this._handleTargetRemoved, this);
287
288         WI.notifications.addEventListener(WI.Notification.ExtraDomainsActivated, this._handleExtraDomainsActivated, this);
289
290         if (WI.SourcesNavigationSidebarPanel.shouldPlaceResourcesAtTopLevel()) {
291             this._resourcesTreeOutline.disclosureButtons = false;
292             WI.SourceCode.addEventListener(WI.SourceCode.Event.SourceMapAdded, () => {
293                 this._resourcesTreeOutline.disclosureButtons = true;
294             }, this);
295         }
296
297         WI.debuggerManager.addBreakpoint(WI.debuggerManager.allExceptionsBreakpoint);
298         WI.debuggerManager.addBreakpoint(WI.debuggerManager.uncaughtExceptionsBreakpoint);
299
300         // COMPATIBILITY (iOS 10): DebuggerAgent.setPauseOnAssertions did not exist yet.
301         if (InspectorBackend.domains.Debugger.setPauseOnAssertions && WI.settings.showAssertionFailuresBreakpoint.value)
302             WI.debuggerManager.addBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint);
303
304         for (let target of WI.targets)
305             this._addTarget(target);
306
307         this._updateCallStackTreeOutline();
308
309         this._handleResourceGroupingModeChanged();
310
311         if (WI.domDebuggerManager.supported) {
312             if (WI.settings.showAllRequestsBreakpoint.value)
313                 WI.domDebuggerManager.addURLBreakpoint(WI.domDebuggerManager.allRequestsBreakpoint);
314
315             for (let eventBreakpoint of WI.domDebuggerManager.eventBreakpoints)
316                 this._addBreakpoint(eventBreakpoint);
317
318             for (let domBreakpoint of WI.domDebuggerManager.domBreakpoints)
319                 this._addBreakpoint(domBreakpoint);
320
321             for (let eventListenerBreakpoint of WI.domManager.eventListenerBreakpoints)
322                 this._addBreakpoint(eventListenerBreakpoint);
323
324             for (let urlBreakpoints of WI.domDebuggerManager.urlBreakpoints)
325                 this._addBreakpoint(urlBreakpoints);
326         }
327
328         if (WI.debuggerManager.paused)
329             this._handleDebuggerPaused();
330
331         if (WI.debuggerManager.breakpointsDisabledTemporarily) {
332             if (WI.timelineManager.isCapturing())
333                 this._handleTimelineCapturingWillStart();
334
335             if (WI.auditManager.runningState === WI.AuditManager.RunningState.Active || WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping)
336                 this._handleAuditManagerTestScheduled();
337         }
338
339         this._updateBreakpointsDisabledBanner();
340     }
341
342     // Static
343
344     static shouldPlaceResourcesAtTopLevel()
345     {
346         return (WI.sharedApp.debuggableType === WI.DebuggableType.JavaScript && !WI.sharedApp.hasExtraDomains)
347             || WI.sharedApp.debuggableType === WI.DebuggableType.ServiceWorker;
348     }
349
350     // Public
351
352     get minimumWidth()
353     {
354         return Math.max(this._debuggerNavigationBar.minimumWidth, this._resourcesNavigationBar.minimumWidth);
355     }
356
357     closed()
358     {
359         WI.settings.resourceGroupingMode.removeEventListener(null, null, this);
360         WI.Frame.removeEventListener(null, null, this);
361         WI.Target.removeEventListener(null, null, this);
362         WI.networkManager.removeEventListener(null, null, this);
363         WI.debuggerManager.removeEventListener(null, null, this);
364         WI.domDebuggerManager.removeEventListener(null, null, this);
365         WI.Breakpoint.removeEventListener(null, null, this);
366         WI.IssueMessage.removeEventListener(null, null, this);
367         WI.DOMBreakpoint.removeEventListener(null, null, this);
368         WI.consoleManager.removeEventListener(null, null, this);
369         WI.timelineManager.removeEventListener(null, null, this);
370         WI.auditManager.removeEventListener(null, null, this);
371         WI.cssManager.removeEventListener(null, null, this);
372         WI.targetManager.removeEventListener(null, null, this);
373         WI.notifications.removeEventListener(null, null, this);
374         WI.SourceCode.removeEventListener(null, null, this);
375
376         super.closed();
377     }
378
379     showDefaultContentView()
380     {
381         if (WI.networkManager.mainFrame) {
382             this.contentBrowser.showContentViewForRepresentedObject(WI.networkManager.mainFrame);
383             return;
384         }
385
386         let firstTreeElement = this._resourcesTreeOutline.children[0];
387         if (firstTreeElement)
388             this.showDefaultContentViewForTreeElement(firstTreeElement);
389     }
390
391     treeElementForRepresentedObject(representedObject)
392     {
393         // A custom implementation is needed for this since the frames are populated lazily.
394
395         if (!this._mainFrameTreeElement && (representedObject instanceof WI.Resource || representedObject instanceof WI.Frame || representedObject instanceof WI.Collection)) {
396             // All resources are under the main frame, so we need to return early if we don't have the main frame yet.
397             return null;
398         }
399
400         if (representedObject instanceof WI.Resource && representedObject.parentFrame && representedObject.parentFrame.mainResource === representedObject)
401             representedObject = representedObject.parentFrame;
402
403         function isAncestor(ancestor, resourceOrFrame) {
404             // SourceMapResources are descendants of another SourceCode object.
405             if (resourceOrFrame instanceof WI.SourceMapResource) {
406                 if (resourceOrFrame.sourceMap.originalSourceCode === ancestor)
407                     return true;
408
409                 // Not a direct ancestor, so check the ancestors of the parent SourceCode object.
410                 resourceOrFrame = resourceOrFrame.sourceMap.originalSourceCode;
411             }
412
413             let currentFrame = resourceOrFrame.parentFrame;
414             while (currentFrame) {
415                 if (currentFrame === ancestor)
416                     return true;
417                 currentFrame = currentFrame.parentFrame;
418             }
419             return false;
420         }
421
422         function getParent(resourceOrFrame) {
423             // SourceMapResources are descendants of another SourceCode object.
424             if (resourceOrFrame instanceof WI.SourceMapResource)
425                 return resourceOrFrame.sourceMap.originalSourceCode;
426             return resourceOrFrame.parentFrame;
427         }
428
429         function searchTreeOutline(treeOutline, forceSearch) {
430             if (!treeOutline || (!treeOutline.selectedTreeElement && !forceSearch))
431                 return null;
432             return treeOutline.findTreeElement(representedObject, isAncestor, getParent);
433         }
434         let treeElement = searchTreeOutline(this._pauseReasonTreeOutline) || searchTreeOutline(this._callStackTreeOutline) || searchTreeOutline(this._breakpointsTreeOutline) || searchTreeOutline(this._resourcesTreeOutline, true);
435         if (treeElement)
436             return treeElement;
437
438         // Only special case Script objects.
439         if (!(representedObject instanceof WI.Script)) {
440             console.error("Didn't find a TreeElement for representedObject", representedObject);
441             return null;
442         }
443
444         // If the Script has a URL we should have found it earlier.
445         if (representedObject.url) {
446             console.error("Didn't find a ScriptTreeElement for a Script with a URL.");
447             return null;
448         }
449
450         // Since the Script does not have a URL we consider it an 'anonymous' script. These scripts happen from calls to
451         // window.eval() or browser features like Auto Fill and Reader. They are not normally added to the sidebar, but since
452         // we have a ScriptContentView asking for the tree element we will make a ScriptTreeElement on demand and add it.
453
454         if (!this._anonymousScriptsFolderTreeElement)
455             this._anonymousScriptsFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Anonymous Scripts"), new WI.ScriptCollection);
456
457         if (!this._anonymousScriptsFolderTreeElement.parent) {
458             let index = insertionIndexForObjectInListSortedByFunction(this._anonymousScriptsFolderTreeElement, this._resourcesTreeOutline.children, this._boundCompareTreeElements);
459             this._resourcesTreeOutline.insertChild(this._anonymousScriptsFolderTreeElement, index);
460         }
461
462         this._anonymousScriptsFolderTreeElement.representedObject.add(representedObject);
463
464         let scriptTreeElement = new WI.ScriptTreeElement(representedObject);
465         this._anonymousScriptsFolderTreeElement.appendChild(scriptTreeElement);
466         return scriptTreeElement;
467     }
468
469     // Protected
470
471     resetFilter()
472     {
473         this._resourceTypeScopeBar.resetToDefault();
474
475         super.resetFilter();
476     }
477
478     hasCustomFilters()
479     {
480         console.assert(this._resourceTypeScopeBar.selectedItems.length === 1);
481         let selectedScopeBarItem = this._resourceTypeScopeBar.selectedItems[0];
482         return selectedScopeBarItem && !selectedScopeBarItem.exclusive;
483     }
484
485     matchTreeElementAgainstCustomFilters(treeElement, flags)
486     {
487         // Only apply the resource type filter to the resources list.
488         if (treeElement.treeOutline !== this._resourcesTreeOutline)
489             return true;
490
491         console.assert(this._resourceTypeScopeBar.selectedItems.length === 1);
492         let selectedScopeBarItem = this._resourceTypeScopeBar.selectedItems[0];
493
494         // Show everything if there is no selection or "All Resources" is selected (the exclusive item).
495         if (!selectedScopeBarItem || selectedScopeBarItem.exclusive)
496             return true;
497
498         // Folders are hidden on the first pass, but visible childen under the folder will force the folder visible again.
499         if (treeElement instanceof WI.FolderTreeElement)
500             return false;
501
502         if (treeElement instanceof WI.IssueTreeElement)
503             treeElement = treeElement.parent;
504
505         function match()
506         {
507             if (treeElement instanceof WI.FrameTreeElement)
508                 return selectedScopeBarItem[WI.SourcesNavigationSidebarPanel.ResourceTypeSymbol] === WI.Resource.Type.Document;
509
510             if (treeElement instanceof WI.ScriptTreeElement)
511                 return selectedScopeBarItem[WI.SourcesNavigationSidebarPanel.ResourceTypeSymbol] === WI.Resource.Type.Script;
512
513             if (treeElement instanceof WI.CSSStyleSheetTreeElement)
514                 return selectedScopeBarItem[WI.SourcesNavigationSidebarPanel.ResourceTypeSymbol] === WI.Resource.Type.Stylesheet;
515
516             console.assert(treeElement instanceof WI.ResourceTreeElement, "Unknown treeElement", treeElement);
517             if (!(treeElement instanceof WI.ResourceTreeElement))
518                 return false;
519
520             return treeElement.resource.type === selectedScopeBarItem[WI.SourcesNavigationSidebarPanel.ResourceTypeSymbol];
521         }
522
523         let matched = match();
524         if (matched)
525             flags.expandTreeElement = true;
526         return matched;
527     }
528
529     // Popover delegate
530
531     willDismissPopover(popover)
532     {
533         let breakpoint = popover.breakpoint;
534         if (!breakpoint)
535             return;
536
537         if (breakpoint instanceof WI.EventBreakpoint)
538             WI.domDebuggerManager.addEventBreakpoint(breakpoint);
539         else if (breakpoint instanceof WI.URLBreakpoint)
540             WI.domDebuggerManager.addURLBreakpoint(breakpoint);
541     }
542
543     // Private
544
545     _compareTreeElements(a, b)
546     {
547         const rankFunctions = [
548             (treeElement) => treeElement === this._mainFrameTreeElement,
549             (treeElement) => treeElement instanceof WI.FrameTreeElement,
550             (treeElement) => {
551                 return treeElement instanceof WI.FolderTreeElement
552                     && treeElement !== this._extensionScriptsFolderTreeElement
553                     && treeElement !== this._extraScriptsFolderTreeElement
554                     && treeElement !== this._anonymousScriptsFolderTreeElement;
555             },
556             (treeElement) => treeElement === this._extensionScriptsFolderTreeElement,
557             (treeElement) => treeElement === this._extraScriptsFolderTreeElement,
558             (treeElement) => treeElement === this._anonymousScriptsFolderTreeElement,
559         ];
560
561         let aRank = rankFunctions.findIndex((rankFunction) => rankFunction(a));
562         let bRank = rankFunctions.findIndex((rankFunction) => rankFunction(b));
563         if (aRank >= 0 && bRank >= 0) {
564             if (aRank < bRank)
565                 return -1;
566             if (bRank < aRank)
567                 return 1;
568         }
569
570         return a.mainTitle.extendedLocaleCompare(b.mainTitle) || a.subtitle.extendedLocaleCompare(b.subtitle);
571     }
572
573     _updateMainFrameTreeElement(mainFrame)
574     {
575         if (this.didInitialLayout)
576             this.contentBrowser.contentViewContainer.closeAllContentViews();
577
578         let oldMainFrameTreeElement = this._mainFrameTreeElement;
579         if (this._mainFrameTreeElement) {
580             this._resourcesTreeOutline.removeChild(this._mainFrameTreeElement);
581             this._mainFrameTreeElement = null;
582         }
583
584         if (!mainFrame)
585             return;
586
587         let resourceGroupingMode = WI.settings.resourceGroupingMode.value;
588         switch (resourceGroupingMode) {
589         case WI.Resource.GroupingMode.Path:
590             for (let treeElement of this._originTreeElementMap.values()) {
591                 if (treeElement !== oldMainFrameTreeElement)
592                     this._resourcesTreeOutline.removeChild(treeElement);
593             }
594             this._originTreeElementMap.clear();
595
596             this._mainFrameTreeElement = new WI.FolderTreeElement(mainFrame.securityOrigin, mainFrame);
597             this._originTreeElementMap.set(mainFrame.securityOrigin, this._mainFrameTreeElement);
598             break;
599
600         default:
601             WI.reportInternalError("Unknown resource grouping mode", {"Resource Grouping Mode": WI.settings.resourceGroupingMode.value});
602             // Fallthrough for default value.
603
604         case WI.Resource.GroupingMode.Type:
605             this._mainFrameTreeElement = new WI.FrameTreeElement(mainFrame);
606             break;
607         }
608
609         this._resourcesTreeOutline.insertChild(this._mainFrameTreeElement, 0);
610
611         // Cookie restoration will attempt to re-select the resource we were showing.
612         // Give it time to do that before selecting the main frame resource.
613         setTimeout(() => {
614             if (this._resourcesTreeOutline.selectedTreeElement)
615                 return;
616
617             let currentContentView = this.contentBrowser.currentContentView;
618             let treeElement = currentContentView ? this.treeElementForRepresentedObject(currentContentView.representedObject) : null;
619             if (!treeElement)
620                 treeElement = this.treeElementForRepresentedObject(WI.networkManager.mainFrame);
621             this.showDefaultContentViewForTreeElement(treeElement);
622         });
623     }
624
625     _addTarget(target)
626     {
627         let treeElement = new WI.ThreadTreeElement(target);
628         this._callStackTreeOutline.appendChild(treeElement);
629
630         // FIXME: When WI.mainTarget changes?
631         if (target === WI.mainTarget)
632             this._mainTargetTreeElement = treeElement;
633
634         this._updateCallStackTreeOutline();
635     }
636
637     _findCallStackTargetTreeElement(target)
638     {
639         for (let child of this._callStackTreeOutline.children) {
640             if (child.target === target)
641                 return child;
642         }
643         return null;
644     }
645
646     _updateCallStackTreeOutline()
647     {
648         let singleThreadShowing = WI.targets.length <= 1;
649         this._callStackTreeOutline.element.classList.toggle("single-thread", singleThreadShowing);
650         if (this._mainTargetTreeElement)
651             this._mainTargetTreeElement.selectable = !singleThreadShowing;
652     }
653
654     _addResource(resource)
655     {
656         if (WI.settings.resourceGroupingMode.value === WI.Resource.GroupingMode.Path) {
657             if (!this._mainFrameTreeElement || this._resourcesTreeOutline.findTreeElement(resource))
658                 return;
659
660             let origin = null;
661             if (resource.urlComponents.scheme && resource.urlComponents.host) {
662                 origin = resource.urlComponents.scheme + "://" + resource.urlComponents.host;
663                 if (resource.urlComponents.port)
664                     origin += ":" + resource.urlComponents.port;
665             } else
666                 origin = resource.parentFrame.securityOrigin;
667
668             let frameTreeElement = this._originTreeElementMap.get(origin);
669             if (!frameTreeElement) {
670                 frameTreeElement = new WI.FolderTreeElement(origin, origin === resource.parentFrame.securityOrigin ? resource.parentFrame : null);
671                 this._originTreeElementMap.set(origin, frameTreeElement);
672
673                 let index = insertionIndexForObjectInListSortedByFunction(frameTreeElement, this._resourcesTreeOutline.children, this._boundCompareTreeElements);
674                 this._resourcesTreeOutline.insertChild(frameTreeElement, index);
675             }
676
677             let subpath = resource.urlComponents.path;
678             if (subpath && subpath[0] === "/")
679                 subpath = subpath.substring(1);
680
681             let parent = frameTreeElement.createFoldersAsNeededForSubpath(subpath);
682             if (resource instanceof WI.CSSStyleSheet)
683                 parent.appendChild(new WI.CSSStyleSheetTreeElement(resource));
684             else
685                 parent.appendChild(new WI.ResourceTreeElement(resource, resource, {allowDirectoryAsName: true, hideOrigin: true}));
686         }
687
688         if (resource.type === WI.Resource.Type.Document || resource.type === WI.Resource.Type.Script) {
689             this._addBreakpointsForSourceCode(resource);
690             this._addIssuesForSourceCode(resource);
691         }
692     }
693
694     _addResourcesRecursivelyForFrame(frame)
695     {
696         this._addResource(frame.mainResource);
697
698         for (let resource of frame.resourceCollection)
699             this._addResource(resource);
700
701         for (let childFrame of frame.childFrameCollection)
702             this._addResourcesRecursivelyForFrame(childFrame);
703     }
704
705     _addScript(script)
706     {
707         // We don't add scripts without URLs here. Those scripts can quickly clutter the interface and
708         // are usually more transient. They will get added if/when they need to be shown in a content view.
709         if (!script.url && !script.sourceURL)
710             return;
711
712         // Target main resource.
713         if (WI.sharedApp.debuggableType !== WI.DebuggableType.JavaScript) {
714             if (script.target !== WI.pageTarget) {
715                 if (script.isMainResource()) {
716                     this._addWorkerTargetWithMainResource(script.target);
717                     this._addBreakpointsForSourceCode(script);
718                     this._addIssuesForSourceCode(script);
719                 }
720                 this._resourcesTreeOutline.disclosureButtons = true;
721                 return;
722             }
723         }
724
725         // If the script URL matches a resource we can assume it is part of that resource and does not need added.
726         if (script.resource || script.dynamicallyAddedScriptElement)
727             return;
728
729         let scriptTreeElement = new WI.ScriptTreeElement(script);
730
731         if (!script.injected && WI.SourcesNavigationSidebarPanel.shouldPlaceResourcesAtTopLevel()) {
732             let index = insertionIndexForObjectInListSortedByFunction(scriptTreeElement, this._resourcesTreeOutline.children, this._boundCompareTreeElements);
733             this._resourcesTreeOutline.insertChild(scriptTreeElement, index);
734         } else {
735             let parentFolderTreeElement = null;
736
737             if (script.injected) {
738                 if (!this._extensionScriptsFolderTreeElement) {
739                     let collection = new WI.ScriptCollection;
740                     this._extensionScriptsFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Extension Scripts"), collection);
741                 }
742
743                 parentFolderTreeElement = this._extensionScriptsFolderTreeElement;
744             } else {
745                 if (!this._extraScriptsFolderTreeElement) {
746                     let collection = new WI.ScriptCollection;
747                     this._extraScriptsFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Extra Scripts"), collection);
748                 }
749
750                 parentFolderTreeElement = this._extraScriptsFolderTreeElement;
751             }
752
753             if (parentFolderTreeElement)
754                 parentFolderTreeElement.representedObject.add(script);
755
756             if (!parentFolderTreeElement.parent) {
757                 let index = insertionIndexForObjectInListSortedByFunction(parentFolderTreeElement, this._resourcesTreeOutline.children, this._boundCompareTreeElements);
758                 this._resourcesTreeOutline.insertChild(parentFolderTreeElement, index);
759             }
760
761             parentFolderTreeElement.appendChild(scriptTreeElement);
762         }
763
764         this._addBreakpointsForSourceCode(script);
765         this._addIssuesForSourceCode(script);
766     }
767
768     _addWorkerTargetWithMainResource(target)
769     {
770         console.assert(target.type === WI.Target.Type.Worker || target.type === WI.Target.Type.ServiceWorker);
771
772         let targetTreeElement = new WI.WorkerTreeElement(target);
773         this._workerTargetTreeElementMap.set(target, targetTreeElement);
774
775         let index = insertionIndexForObjectInListSortedByFunction(targetTreeElement, this._resourcesTreeOutline.children, this._boundCompareTreeElements);
776         this._resourcesTreeOutline.insertChild(targetTreeElement, index);
777     }
778
779     _addDebuggerTreeElementForSourceCode(sourceCode)
780     {
781         let treeElement = this._breakpointsTreeOutline.findTreeElement(sourceCode);
782         if (!treeElement) {
783             if (sourceCode instanceof WI.SourceMapResource)
784                 treeElement = new WI.SourceMapResourceTreeElement(sourceCode);
785             else if (sourceCode instanceof WI.Resource)
786                 treeElement = new WI.ResourceTreeElement(sourceCode);
787             else if (sourceCode instanceof WI.Script)
788                 treeElement = new WI.ScriptTreeElement(sourceCode);
789         }
790
791         if (!treeElement) {
792             console.error("Unknown sourceCode instance", sourceCode);
793             return null;
794         }
795
796         if (!treeElement.parent) {
797             treeElement.hasChildren = false;
798             treeElement.expand();
799
800             this._insertDebuggerTreeElement(treeElement, this._breakpointsTreeOutline);
801         }
802
803         return treeElement;
804     }
805
806     _insertDebuggerTreeElement(treeElement, parentTreeElement)
807     {
808         let comparator = (a, b) => {
809             const rankFunctions = [
810                 (treeElement) => treeElement.representedObject === WI.debuggerManager.allExceptionsBreakpoint,
811                 (treeElement) => treeElement.representedObject === WI.debuggerManager.uncaughtExceptionsBreakpoint,
812                 (treeElement) => treeElement.representedObject === WI.debuggerManager.assertionFailuresBreakpoint,
813                 (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allRequestsBreakpoint,
814                 (treeElement) => treeElement instanceof WI.BreakpointTreeElement || treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement,
815                 (treeElement) => treeElement instanceof WI.EventBreakpointTreeElement,
816                 (treeElement) => treeElement instanceof WI.DOMNodeTreeElement,
817                 (treeElement) => treeElement instanceof WI.DOMBreakpointTreeElement,
818                 (treeElement) => treeElement instanceof WI.URLBreakpointTreeElement,
819             ];
820
821             let aRank = rankFunctions.findIndex((rankFunction) => rankFunction(a));
822             let bRank = rankFunctions.findIndex((rankFunction) => rankFunction(b));
823             if (aRank >= 0 && bRank >= 0) {
824                 if (aRank < bRank)
825                     return -1;
826                 if (bRank < aRank)
827                     return 1;
828             }
829
830             if (a instanceof WI.BreakpointTreeElement && b instanceof WI.BreakpointTreeElement)
831                 return this._compareBreakpointTreeElements(a, b);
832
833             return a.mainTitle.extendedLocaleCompare(b.mainTitle) || a.subtitle.extendedLocaleCompare(b.subtitle);
834         };
835
836         parentTreeElement.insertChild(treeElement, insertionIndexForObjectInListSortedByFunction(treeElement, parentTreeElement.children, comparator));
837     }
838
839     _compareBreakpointTreeElements(a, b)
840     {
841         if (!a.representedObject || !b.representedObject)
842             return 0;
843
844         let aLocation = a.representedObject.sourceCodeLocation;
845         let bLocation = b.representedObject.sourceCodeLocation;
846         if (!aLocation || !bLocation)
847             return 0;
848
849         return aLocation.displayLineNumber - bLocation.displayLineNumber || aLocation.displayColumnNumber - bLocation.displayColumnNumber;
850     }
851
852     _addBreakpoint(breakpoint)
853     {
854         let constructor = WI.BreakpointTreeElement;
855         let options = {};
856         let parentTreeElement = this._breakpointsTreeOutline;
857
858         let getDOMNodeTreeElement = (domNode) => {
859             console.assert(domNode, "Missing DOMNode for identifier", breakpoint.domNodeIdentifier);
860             if (!domNode)
861                 return null;
862
863             let domNodeTreeElement = this._breakpointsTreeOutline.findTreeElement(domNode);
864             if (!domNodeTreeElement) {
865                 domNodeTreeElement = new WI.DOMNodeTreeElement(domNode);
866                 this._insertDebuggerTreeElement(domNodeTreeElement, parentTreeElement);
867             }
868             return domNodeTreeElement;
869         };
870
871         if (breakpoint === WI.debuggerManager.allExceptionsBreakpoint) {
872             options.className = "breakpoint-exception-icon";
873             options.title = WI.UIString("All Exceptions");
874         } else if (breakpoint === WI.debuggerManager.uncaughtExceptionsBreakpoint) {
875             options.className = "breakpoint-exception-icon";
876             options.title = WI.UIString("Uncaught Exceptions");
877         } else if (breakpoint === WI.debuggerManager.assertionFailuresBreakpoint) {
878             options.className = "breakpoint-assertion-icon";
879             options.title = WI.UIString("Assertion Failures");
880         } else if (breakpoint instanceof WI.DOMBreakpoint) {
881             if (!breakpoint.domNodeIdentifier)
882                 return null;
883
884             constructor = WI.DOMBreakpointTreeElement;
885
886             let domNode = WI.domManager.nodeForId(breakpoint.domNodeIdentifier);
887             parentTreeElement = getDOMNodeTreeElement(domNode);
888         } else if (breakpoint instanceof WI.EventBreakpoint) {
889             constructor = WI.EventBreakpointTreeElement;
890
891             if (breakpoint.eventListener)
892                 parentTreeElement = getDOMNodeTreeElement(breakpoint.eventListener.node);
893         } else if (breakpoint instanceof WI.URLBreakpoint) {
894             constructor = WI.URLBreakpointTreeElement;
895
896             if (breakpoint === WI.domDebuggerManager.allRequestsBreakpoint) {
897                 options.className = "breakpoint-assertion-icon";
898                 options.title = WI.UIString("All Requests");
899             }
900         } else {
901             let sourceCode = breakpoint.sourceCodeLocation && breakpoint.sourceCodeLocation.displaySourceCode;
902             if (!sourceCode)
903                 return null;
904
905             if (this._breakpointsTreeOutline.findTreeElement(breakpoint))
906                 return null;
907
908             parentTreeElement = this._addDebuggerTreeElementForSourceCode(sourceCode);
909
910             // Mark disabled breakpoints as resolved if there is source code loaded with that URL.
911             // This gives the illusion the breakpoint was resolved, but since we don't send disabled
912             // breakpoints to the backend we don't know for sure. If the user enables the breakpoint
913             // it will be resolved properly.
914             if (breakpoint.disabled)
915                 breakpoint.resolved = true;
916         }
917
918         let breakpointTreeElement = new constructor(breakpoint, options);
919         this._insertDebuggerTreeElement(breakpointTreeElement, parentTreeElement);
920         if (parentTreeElement.children.length === 1)
921             parentTreeElement.expand();
922         return breakpointTreeElement;
923     }
924
925     _removeBreakpoint(breakpoint)
926     {
927         if (this._pauseReasonTreeOutline) {
928             let pauseReasonBreakpointTreeElement = this._pauseReasonTreeOutline.findTreeElement(breakpoint);
929             if (pauseReasonBreakpointTreeElement)
930                 pauseReasonBreakpointTreeElement.status = null;
931         }
932
933         let breakpointTreeElement = this._breakpointsTreeOutline.findTreeElement(breakpoint);
934         if (!breakpointTreeElement)
935             return;
936
937         this._removeDebuggerTreeElement(breakpointTreeElement);
938     }
939
940     _removeAllBreakpoints(breakpoints)
941     {
942         for (let breakpoint of breakpoints) {
943             if (WI.debuggerManager.isBreakpointRemovable(breakpoint))
944                 WI.debuggerManager.removeBreakpoint(breakpoint);
945         }
946     }
947
948     _toggleAllBreakpoints(breakpoints, disabled)
949     {
950         for (let breakpoint of breakpoints)
951             breakpoint.disabled = disabled;
952     }
953
954     _breakpointsBeneathTreeElement(treeElement)
955     {
956         console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement);
957         if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
958             return [];
959
960         let breakpoints = [];
961         for (let child of treeElement.children) {
962             console.assert(child instanceof WI.BreakpointTreeElement);
963             let breakpoint = child.breakpoint;
964             console.assert(breakpoint);
965             if (breakpoint)
966                 breakpoints.push(breakpoint);
967         }
968         return breakpoints;
969     }
970
971     _addIssue(issueMessage)
972     {
973         let issueTreeElement = this._resourcesTreeOutline.findTreeElement(issueMessage);
974         if (!issueTreeElement) {
975             let parentTreeElement = this._resourcesTreeOutline.findTreeElement(issueMessage.sourceCodeLocation.sourceCode);
976             if (!parentTreeElement)
977                 return null;
978
979             issueTreeElement = new WI.IssueTreeElement(issueMessage);
980
981             parentTreeElement.insertChild(issueTreeElement, insertionIndexForObjectInListSortedByFunction(issueTreeElement, parentTreeElement.children, this._compareBreakpointTreeElements));
982             if (parentTreeElement.children.length === 1)
983                 parentTreeElement.expand();
984         }
985         return issueTreeElement;
986     }
987
988     _removeDebuggerTreeElement(debuggerTreeElement)
989     {
990         // If this is a BreakpointTreeElement being deleted because of a cause
991         // outside of the TreeOutline then deselect if it is selected to avoid
992         // TreeOutline selection changes causing unexpected ContentView changes.
993         if (!debuggerTreeElement.__deletedViaDeleteKeyboardShortcut)
994             debuggerTreeElement.deselect();
995
996         let parentTreeElement = debuggerTreeElement.parent;
997         parentTreeElement.removeChild(debuggerTreeElement);
998
999         if (parentTreeElement.children.length || parentTreeElement === this._breakpointsTreeOutline)
1000             return;
1001
1002         parentTreeElement.treeOutline.removeChild(parentTreeElement);
1003     }
1004
1005     _addBreakpointsForSourceCode(sourceCode)
1006     {
1007         for (let breakpoint of WI.debuggerManager.breakpointsForSourceCode(sourceCode))
1008             this._addBreakpoint(breakpoint, sourceCode);
1009     }
1010
1011     _addIssuesForSourceCode(sourceCode)
1012     {
1013         for (let issue of WI.consoleManager.issuesForSourceCode(sourceCode))
1014             this._addIssue(issue);
1015     }
1016
1017     _updateTemporarilyDisabledBreakpointsButtons()
1018     {
1019         let breakpointsDisabledTemporarily = WI.debuggerManager.breakpointsDisabledTemporarily;
1020         this._debuggerBreakpointsButtonItem.enabled = !breakpointsDisabledTemporarily;
1021         this._debuggerPauseResumeButtonItem.enabled = !breakpointsDisabledTemporarily;
1022     }
1023
1024     _updateBreakpointsDisabledBanner()
1025     {
1026         if (!WI.debuggerManager.breakpointsEnabled && !this._timelineRecordingWarningElement && !this._auditTestWarningElement) {
1027             if (!this._breakpointsDisabledWarningElement) {
1028                 let enableBreakpointsButton = document.createElement("button");
1029                 enableBreakpointsButton.textContent = WI.UIString("Enable Breakpoints");
1030                 enableBreakpointsButton.addEventListener("click", () => {
1031                     WI.debuggerToggleBreakpoints();
1032                 });
1033
1034                 this._breakpointsDisabledWarningElement = document.createElement("div");
1035                 this._breakpointsDisabledWarningElement.classList.add("warning-banner");
1036                 this._breakpointsDisabledWarningElement.append(WI.UIString("Breakpoints disabled"), document.createElement("br"), enableBreakpointsButton);
1037             }
1038
1039             this.contentView.element.insertBefore(this._breakpointsDisabledWarningElement, this.contentView.element.firstChild);
1040         } else if (this._breakpointsDisabledWarningElement) {
1041             this._breakpointsDisabledWarningElement.remove();
1042             this._breakpointsDisabledWarningElement = null;
1043         }
1044     }
1045
1046     _updatePauseReason()
1047     {
1048         this._pauseReasonTreeOutline = null;
1049
1050         this._updatePauseReasonGotoArrow();
1051         return this._updatePauseReasonSection();
1052     }
1053
1054     _updatePauseReasonGotoArrow()
1055     {
1056         this._pauseReasonLinkContainerElement.removeChildren();
1057
1058         let activeCallFrame = WI.debuggerManager.activeCallFrame;
1059         if (!activeCallFrame)
1060             return;
1061
1062         let sourceCodeLocation = activeCallFrame.sourceCodeLocation;
1063         if (!sourceCodeLocation)
1064             return;
1065
1066         const options = {
1067             useGoToArrowButton: true,
1068         };
1069         let linkElement = WI.createSourceCodeLocationLink(sourceCodeLocation, options);
1070         this._pauseReasonLinkContainerElement.appendChild(linkElement);
1071     }
1072
1073     _updatePauseReasonSection()
1074     {
1075         let target = WI.debuggerManager.activeCallFrame.target;
1076         let targetData = WI.debuggerManager.dataForTarget(target);
1077         let {pauseReason, pauseData} = targetData;
1078
1079         switch (pauseReason) {
1080         case WI.DebuggerManager.PauseReason.AnimationFrame: {
1081             console.assert(pauseData, "Expected data with an animation frame, but found none.");
1082             if (!pauseData)
1083                 break;
1084
1085             let eventBreakpoint = WI.domDebuggerManager.eventBreakpointForTypeAndEventName(WI.EventBreakpoint.Type.AnimationFrame, pauseData.eventName);
1086             console.assert(eventBreakpoint, "Expected AnimationFrame breakpoint for event name.", pauseData.eventName);
1087             if (!eventBreakpoint)
1088                 break;
1089
1090             this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1091
1092             let eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, {
1093                 className: "breakpoint-paused-icon",
1094                 title: WI.UIString("%s Fired").format(pauseData.eventName),
1095             });
1096             this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
1097
1098             let eventBreakpointRow = new WI.DetailsSectionRow;
1099             eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1100
1101             this._pauseReasonGroup.rows = [eventBreakpointRow];
1102             return true;
1103         }
1104
1105         case WI.DebuggerManager.PauseReason.Assertion:
1106             // FIXME: We should include the assertion condition string.
1107             console.assert(pauseData, "Expected data with an assertion, but found none.");
1108             if (pauseData && pauseData.message)
1109                 this._pauseReasonTextRow.text = WI.UIString("Assertion with message: %s").format(pauseData.message);
1110             else
1111                 this._pauseReasonTextRow.text = WI.UIString("Assertion Failed");
1112             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1113             return true;
1114
1115         case WI.DebuggerManager.PauseReason.Breakpoint: {
1116             console.assert(pauseData, "Expected breakpoint identifier, but found none.");
1117             if (!pauseData || !pauseData.breakpointId)
1118                 break;
1119
1120             this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1121             this._pauseReasonTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._handleTreeSelectionDidChange, this);
1122
1123             let breakpoint = WI.debuggerManager.breakpointForIdentifier(pauseData.breakpointId);
1124             let breakpointTreeElement = new WI.BreakpointTreeElement(breakpoint, {
1125                 className: "breakpoint-paused-icon",
1126                 title: WI.UIString("Triggered Breakpoint"),
1127             });
1128             let breakpointDetailsSection = new WI.DetailsSectionRow;
1129             this._pauseReasonTreeOutline.appendChild(breakpointTreeElement);
1130             breakpointDetailsSection.element.appendChild(this._pauseReasonTreeOutline.element);
1131
1132             this._pauseReasonGroup.rows = [breakpointDetailsSection];
1133             return true;
1134         }
1135
1136         case WI.DebuggerManager.PauseReason.CSPViolation:
1137             console.assert(pauseData, "Expected data with a CSP Violation, but found none.");
1138             if (!pauseData)
1139                 break;
1140
1141             // COMPATIBILITY (iOS 8): 'directive' was 'directiveText'.
1142             this._pauseReasonTextRow.text = WI.UIString("Content Security Policy violation of directive: %s").format(pauseData.directive || pauseData.directiveText);
1143             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1144             return true;
1145
1146         case WI.DebuggerManager.PauseReason.DebuggerStatement:
1147             this._pauseReasonTextRow.text = WI.UIString("Debugger Statement");
1148             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1149             return true;
1150
1151         case WI.DebuggerManager.PauseReason.DOM: {
1152             console.assert(WI.domDebuggerManager.supported);
1153             console.assert(pauseData, "Expected DOM breakpoint data, but found none.");
1154             if (!pauseData || !pauseData.nodeId)
1155                 break;
1156
1157             let domNode = WI.domManager.nodeForId(pauseData.nodeId);
1158             let domBreakpoints = WI.domDebuggerManager.domBreakpointsForNode(domNode);
1159             let domBreakpoint;
1160             for (let breakpoint of domBreakpoints) {
1161                 if (breakpoint.type === pauseData.type) {
1162                     domBreakpoint = breakpoint;
1163                     break;
1164                 }
1165             }
1166
1167             console.assert(domBreakpoint, "Missing DOM breakpoint of type for node", pauseData.type, domNode);
1168             if (!domBreakpoint)
1169                 break;
1170
1171             this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1172
1173             let type = WI.DOMBreakpointTreeElement.displayNameForType(domBreakpoint.type);
1174             let domBreakpointTreeElement = new WI.DOMBreakpointTreeElement(domBreakpoint, {
1175                 className: "breakpoint-paused-icon",
1176                 title: type,
1177             });
1178             let domBreakpointRow = new WI.DetailsSectionRow;
1179             this._pauseReasonTreeOutline.appendChild(domBreakpointTreeElement);
1180             domBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1181
1182             let ownerElementRow = new WI.DetailsSectionSimpleRow(WI.UIString("Element"), WI.linkifyNodeReference(domNode));
1183             this._pauseReasonGroup.rows = [domBreakpointRow, ownerElementRow];
1184
1185             if (domBreakpoint.type !== WI.DOMBreakpoint.Type.SubtreeModified)
1186                 return true;
1187
1188             console.assert(pauseData.targetNode);
1189
1190             let remoteObject = WI.RemoteObject.fromPayload(pauseData.targetNode, target);
1191             remoteObject.pushNodeToFrontend((nodeId) => {
1192                 if (!nodeId)
1193                     return;
1194
1195                 let node = WI.domManager.nodeForId(nodeId);
1196                 console.assert(node, "Missing node for id.", nodeId);
1197                 if (!node)
1198                     return;
1199
1200                 let fragment = document.createDocumentFragment();
1201                 let description = pauseData.insertion ? WI.UIString("Child added to ") : WI.UIString("Removed descendant ");
1202                 fragment.append(description, WI.linkifyNodeReference(node));
1203
1204                 let targetDescriptionRow = new WI.DetailsSectionSimpleRow(WI.UIString("Details"), fragment);
1205                 targetDescriptionRow.element.classList.add("target-description");
1206
1207                 this._pauseReasonGroup.rows = this._pauseReasonGroup.rows.concat(targetDescriptionRow);
1208             });
1209
1210             return true;
1211         }
1212
1213         case WI.DebuggerManager.PauseReason.EventListener: {
1214             console.assert(pauseData, "Expected data with an event listener, but found none.");
1215             if (!pauseData)
1216                 break;
1217
1218             let eventBreakpoint = null;
1219             if (pauseData.eventListenerId)
1220                 eventBreakpoint = WI.domManager.breakpointForEventListenerId(pauseData.eventListenerId);
1221             if (!eventBreakpoint)
1222                 eventBreakpoint = WI.domDebuggerManager.eventBreakpointForTypeAndEventName(WI.EventBreakpoint.Type.Listener, pauseData.eventName);
1223
1224             console.assert(eventBreakpoint, "Expected Event Listener breakpoint for event name.", pauseData.eventName);
1225             if (!eventBreakpoint)
1226                 break;
1227
1228             this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1229
1230             let eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, {
1231                 className: "breakpoint-paused-icon",
1232                 title: WI.UIString("\u201C%s\u201D Event Fired").format(pauseData.eventName),
1233             });
1234             this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
1235
1236             let eventBreakpointRow = new WI.DetailsSectionRow;
1237             eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1238
1239             let rows = [eventBreakpointRow];
1240
1241             let eventListener = eventBreakpoint.eventListener;
1242             if (eventListener) {
1243                 console.assert(eventListener.eventListenerId === pauseData.eventListenerId);
1244
1245                 let ownerElementRow = new WI.DetailsSectionSimpleRow(WI.UIString("Element"), WI.linkifyNodeReference(eventListener.node));
1246                 rows.push(ownerElementRow);
1247             }
1248
1249             this._pauseReasonGroup.rows = rows;
1250             return true;
1251         }
1252
1253         case WI.DebuggerManager.PauseReason.Exception: {
1254             console.assert(pauseData, "Expected data with an exception, but found none.");
1255             if (!pauseData)
1256                 break;
1257
1258             // FIXME: We should improve the appearance of thrown objects. This works well for exception strings.
1259             let data = WI.RemoteObject.fromPayload(pauseData, target);
1260             this._pauseReasonTextRow.text = WI.UIString("Exception with thrown value: %s").format(data.description);
1261             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1262             return true;
1263         }
1264
1265         case WI.DebuggerManager.PauseReason.PauseOnNextStatement:
1266             this._pauseReasonTextRow.text = WI.UIString("Immediate Pause Requested");
1267             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1268             return true;
1269
1270         case WI.DebuggerManager.PauseReason.Timer: {
1271             console.assert(pauseData, "Expected data with a timer, but found none.");
1272             if (!pauseData)
1273                 break;
1274
1275             let eventBreakpoint = WI.domDebuggerManager.eventBreakpointForTypeAndEventName(WI.EventBreakpoint.Type.Timer, pauseData.eventName);
1276             console.assert(eventBreakpoint, "Expected Timer breakpoint for event name.", pauseData.eventName);
1277             if (!eventBreakpoint)
1278                 break;
1279
1280             this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1281
1282             let eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, {
1283                 className: "breakpoint-paused-icon",
1284                 title: WI.UIString("%s Fired").format(pauseData.eventName),
1285             });
1286             this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
1287
1288             let eventBreakpointRow = new WI.DetailsSectionRow;
1289             eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1290
1291             this._pauseReasonGroup.rows = [eventBreakpointRow];
1292             return true;
1293         }
1294
1295         case WI.DebuggerManager.PauseReason.Fetch:
1296         case WI.DebuggerManager.PauseReason.XHR: {
1297             console.assert(WI.domDebuggerManager.supported);
1298             console.assert(pauseData, "Expected URL breakpoint data, but found none.");
1299             if (!pauseData)
1300                 break;
1301
1302             if (pauseData.breakpointURL) {
1303                 let urlBreakpoint = WI.domDebuggerManager.urlBreakpointForURL(pauseData.breakpointURL);
1304                 console.assert(urlBreakpoint, "Expected URL breakpoint for URL.", pauseData.breakpointURL);
1305
1306                 this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
1307
1308                 let urlBreakpointTreeElement = new WI.URLBreakpointTreeElement(urlBreakpoint, {
1309                     className: "breakpoint-paused-icon",
1310                     title: WI.UIString("Triggered URL Breakpoint"),
1311                 });
1312                 let urlBreakpointRow = new WI.DetailsSectionRow;
1313                 this._pauseReasonTreeOutline.appendChild(urlBreakpointTreeElement);
1314                 urlBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1315
1316                 this._pauseReasonTextRow.text = pauseData.url;
1317                 this._pauseReasonGroup.rows = [urlBreakpointRow, this._pauseReasonTextRow];
1318             } else {
1319                 console.assert(pauseData.breakpointURL === "", "Should be the All Requests breakpoint which has an empty URL");
1320                 this._pauseReasonTextRow.text = WI.UIString("Requesting: %s").format(pauseData.url);
1321                 this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1322             }
1323             this._pauseReasonTextRow.element.title = pauseData.url;
1324             return true;
1325         }
1326
1327         case WI.DebuggerManager.PauseReason.Other:
1328             console.error("Paused for unknown reason. We should always have a reason.");
1329             break;
1330         }
1331
1332         return false;
1333     }
1334
1335     _handleResourceTypeScopeBarSelectionChanged(event)
1336     {
1337         this.updateFilter();
1338     }
1339
1340     _handleResourceGroupingModeMouseDown(event)
1341     {
1342         if (this._ignoreResourceGroupingModeMouseDown)
1343             return;
1344
1345         this._ignoreResourceGroupingModeMouseDown = true;
1346
1347         let contextMenu = WI.ContextMenu.createFromEvent(event);
1348         contextMenu.addBeforeShowCallback(() => {
1349             this._ignoreResourceGroupingModeMouseDown = false;
1350         });
1351
1352         function addOption(mode, label) {
1353             contextMenu.appendCheckboxItem(label, () => {
1354                 WI.settings.resourceGroupingMode.value = mode;
1355             }, WI.settings.resourceGroupingMode.value === mode);
1356         }
1357         addOption(WI.Resource.GroupingMode.Path, WI.UIString("Group by Path"));
1358         addOption(WI.Resource.GroupingMode.Type, WI.UIString("Group by Type"));
1359
1360         contextMenu.show();
1361     }
1362
1363     _handleTreeSelectionDidChange(event)
1364     {
1365         if (!this.selected)
1366             return;
1367
1368         let treeElement = event.target.selectedTreeElement;
1369         if (!treeElement)
1370             return;
1371
1372         if (treeElement instanceof WI.DOMNodeTreeElement
1373             || treeElement instanceof WI.DOMBreakpointTreeElement
1374             || treeElement instanceof WI.EventBreakpointTreeElement
1375             || treeElement instanceof WI.URLBreakpointTreeElement)
1376             return;
1377
1378         if (treeElement instanceof WI.FolderTreeElement
1379             || treeElement instanceof WI.ResourceTreeElement
1380             || treeElement instanceof WI.ScriptTreeElement
1381             || treeElement instanceof WI.CSSStyleSheetTreeElement) {
1382             let representedObject = treeElement.representedObject;
1383             if (representedObject instanceof WI.Collection || representedObject instanceof WI.SourceCode)
1384                 WI.showRepresentedObject(representedObject);
1385             return;
1386         }
1387
1388         if (treeElement instanceof WI.CallFrameTreeElement) {
1389             let callFrame = treeElement.callFrame;
1390             if (callFrame.id)
1391                 WI.debuggerManager.activeCallFrame = callFrame;
1392             if (callFrame.sourceCodeLocation)
1393                 WI.showSourceCodeLocation(callFrame.sourceCodeLocation);
1394             return;
1395         }
1396
1397         if (treeElement instanceof WI.IssueTreeElement) {
1398             WI.showSourceCodeLocation(treeElement.issueMessage.sourceCodeLocation);
1399             return;
1400         }
1401
1402         if (treeElement instanceof WI.BreakpointTreeElement) {
1403             let breakpoint = treeElement.breakpoint;
1404             if (WI.debuggerManager.isBreakpointSpecial(breakpoint))
1405                 return;
1406
1407             if (treeElement.treeOutline === this._pauseReasonTreeOutline) {
1408                 WI.showSourceCodeLocation(breakpoint.sourceCodeLocation);
1409                 return;
1410             }
1411
1412             if (treeElement.parent.representedObject) {
1413                 console.assert(treeElement.parent.representedObject instanceof WI.SourceCode);
1414                 if (treeElement.parent.representedObject instanceof WI.SourceCode) {
1415                     WI.showSourceCodeLocation(breakpoint.sourceCodeLocation);
1416                     return;
1417                 }
1418             }
1419         }
1420
1421         console.error("Unknown tree element", treeElement);
1422     }
1423
1424     _handleBreakpointElementAddedOrRemoved(event)
1425     {
1426         let treeElement = event.data.element;
1427
1428         let setting = null;
1429         if (treeElement.breakpoint === WI.debuggerManager.assertionFailuresBreakpoint)
1430             setting = WI.settings.showAssertionFailuresBreakpoint;
1431         else if (treeElement.representedObject === WI.domDebuggerManager.allRequestsBreakpoint)
1432             setting = WI.settings.showAllRequestsBreakpoint;
1433
1434         if (setting)
1435             setting.value = !!treeElement.parent;
1436     }
1437
1438     _handleCreateBreakpointMouseDown(event)
1439     {
1440         if (this._ignoreCreateBreakpointMouseDown)
1441             return;
1442
1443         this._ignoreCreateBreakpointMouseDown = true;
1444
1445         let contextMenu = WI.ContextMenu.createFromEvent(event);
1446         contextMenu.addBeforeShowCallback(() => {
1447             this._ignoreCreateBreakpointMouseDown = false;
1448         });
1449
1450         // COMPATIBILITY (iOS 10): DebuggerAgent.setPauseOnAssertions did not exist yet.
1451         if (InspectorBackend.domains.Debugger.setPauseOnAssertions) {
1452             let assertionFailuresBreakpointShown = WI.settings.showAssertionFailuresBreakpoint.value;
1453
1454             contextMenu.appendCheckboxItem(WI.UIString("Assertion Failures"), () => {
1455                 if (assertionFailuresBreakpointShown)
1456                     WI.debuggerManager.removeBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint);
1457                 else {
1458                     WI.debuggerManager.assertionFailuresBreakpoint.disabled = false;
1459                     WI.debuggerManager.addBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint);
1460                 }
1461             }, assertionFailuresBreakpointShown);
1462         }
1463
1464         if (WI.domDebuggerManager.supported) {
1465             contextMenu.appendSeparator();
1466
1467             contextMenu.appendItem(WI.UIString("Event Breakpoint\u2026"), () => {
1468                 let popover = new WI.EventBreakpointPopover(this);
1469                 popover.show(this._createBreakpointButton.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]);
1470             });
1471
1472             contextMenu.appendSeparator();
1473
1474             let allRequestsBreakpointShown = WI.settings.showAllRequestsBreakpoint.value;
1475
1476             contextMenu.appendCheckboxItem(WI.UIString("All Requests"), () => {
1477                 if (allRequestsBreakpointShown)
1478                     WI.domDebuggerManager.removeURLBreakpoint(WI.domDebuggerManager.allRequestsBreakpoint);
1479                 else {
1480                     WI.domDebuggerManager.allRequestsBreakpoint.disabled = false;
1481                     WI.domDebuggerManager.addURLBreakpoint(WI.domDebuggerManager.allRequestsBreakpoint);
1482                 }
1483             }, allRequestsBreakpointShown);
1484
1485             contextMenu.appendItem(WI.UIString("URL Breakpoint\u2026"), () => {
1486                 let popover = new WI.URLBreakpointPopover(this);
1487                 popover.show(this._createBreakpointButton.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]);
1488             });
1489         }
1490
1491         contextMenu.show();
1492     }
1493
1494     _handleResourceGroupingModeChanged(event)
1495     {
1496         this._workerTargetTreeElementMap.clear();
1497         this._mainFrameTreeElement = null;
1498         this._extensionScriptsFolderTreeElement = null;
1499         this._extraScriptsFolderTreeElement = null;
1500         this._anonymousScriptsFolderTreeElement = null;
1501
1502         this._originTreeElementMap.clear();
1503
1504         this._resourcesTreeOutline.removeChildren();
1505
1506         let mainFrame = WI.networkManager.mainFrame;
1507         if (mainFrame) {
1508             this._updateMainFrameTreeElement(mainFrame);
1509             this._addResourcesRecursivelyForFrame(mainFrame);
1510         }
1511
1512         for (let script of WI.debuggerManager.knownNonResourceScripts) {
1513             this._addScript(script);
1514
1515             if (script.sourceMaps.length && WI.SourcesNavigationSidebarPanel.shouldPlaceResourcesAtTopLevel())
1516                 this._resourcesTreeOutline.disclosureButtons = true;
1517         }
1518     }
1519
1520     _handleFrameMainResourceDidChange(event)
1521     {
1522         let frame = event.target;
1523         if (frame.isMainFrame()) {
1524             this._updateMainFrameTreeElement(frame);
1525             this._addResourcesRecursivelyForFrame(frame);
1526
1527             for (let domBreakpoint of WI.domDebuggerManager.domBreakpoints)
1528                 this._removeBreakpoint(domBreakpoint);
1529         }
1530
1531         if (!event.data.oldMainResource) {
1532             let resource = event.target.mainResource;
1533             this._addBreakpointsForSourceCode(resource);
1534             this._addIssuesForSourceCode(resource);
1535         }
1536     }
1537
1538     _handleResourceAdded(event)
1539     {
1540         this._addResource(event.data.resource);
1541     }
1542
1543     _handleMainFrameDidChange(event)
1544     {
1545         let mainFrame = WI.networkManager.mainFrame;
1546         this._updateMainFrameTreeElement(mainFrame);
1547         this._addResourcesRecursivelyForFrame(mainFrame);
1548     }
1549
1550     _handleDebuggerBreakpointAdded(event)
1551     {
1552         this._addBreakpoint(event.data.breakpoint);
1553     }
1554
1555     _handleDebuggerBreakpointRemoved(event)
1556     {
1557         this._removeBreakpoint(event.data.breakpoint);
1558     }
1559
1560     _handleDebuggerBreakpointsEnabledDidChange(event)
1561     {
1562         this._debuggerBreakpointsButtonItem.activated = WI.debuggerManager.breakpointsEnabled;
1563
1564         this._updateBreakpointsDisabledBanner();
1565     }
1566
1567     _handleDebuggerScriptAdded(event)
1568     {
1569         this._addScript(event.data.script);
1570     }
1571
1572     _handleDebuggerScriptRemoved(event)
1573     {
1574         let script = event.data.script;
1575         let scriptTreeElement = this._resourcesTreeOutline.findTreeElement(script);
1576         if (!scriptTreeElement)
1577             return;
1578
1579         let parentTreeElement = scriptTreeElement.parent;
1580         parentTreeElement.removeChild(scriptTreeElement);
1581
1582         if (parentTreeElement instanceof WI.FolderTreeElement) {
1583             parentTreeElement.representedObject.remove(script);
1584
1585             if (!parentTreeElement.children.length)
1586                 parentTreeElement.parent.removeChild(parentTreeElement);
1587         }
1588     }
1589
1590     _handleDebuggerScriptsCleared(event)
1591     {
1592         const suppressOnDeselect = true;
1593         const suppressSelectSibling = true;
1594
1595         for (let i = this._breakpointsTreeOutline.children.length - 1; i >= 0; --i) {
1596             let treeElement = this._breakpointsTreeOutline.children[i];
1597             if (!(treeElement instanceof WI.ScriptTreeElement))
1598                 continue;
1599
1600             this._breakpointsTreeOutline.removeChild(treeElement, suppressOnDeselect, suppressSelectSibling);
1601         }
1602
1603         if (this._extensionScriptsFolderTreeElement) {
1604             if (this._extensionScriptsFolderTreeElement.parent)
1605                 this._extensionScriptsFolderTreeElement.parent.removeChild(this._extensionScriptsFolderTreeElement, suppressOnDeselect, suppressSelectSibling);
1606
1607             this._extensionScriptsFolderTreeElement.representedObject.clear();
1608             this._extensionScriptsFolderTreeElement = null;
1609         }
1610
1611         if (this._extraScriptsFolderTreeElement) {
1612             if (this._extraScriptsFolderTreeElement.parent)
1613                 this._extraScriptsFolderTreeElement.parent.removeChild(this._extraScriptsFolderTreeElement, suppressOnDeselect, suppressSelectSibling);
1614
1615             this._extraScriptsFolderTreeElement.representedObject.clear();
1616             this._extraScriptsFolderTreeElement = null;
1617         }
1618
1619         if (this._anonymousScriptsFolderTreeElement) {
1620             if (this._anonymousScriptsFolderTreeElement.parent)
1621                 this._anonymousScriptsFolderTreeElement.parent.removeChild(this._anonymousScriptsFolderTreeElement, suppressOnDeselect, suppressSelectSibling);
1622
1623             this._anonymousScriptsFolderTreeElement.representedObject.clear();
1624             this._anonymousScriptsFolderTreeElement = null;
1625         }
1626
1627         if (this._workerTargetTreeElementMap.size) {
1628             for (let treeElement of this._workerTargetTreeElementMap.values())
1629                 treeElement.parent.removeChild(treeElement, suppressOnDeselect, suppressSelectSibling);
1630             this._workerTargetTreeElementMap.clear();
1631         }
1632
1633         this._addResourcesRecursivelyForFrame(WI.networkManager.mainFrame);
1634     }
1635
1636     _handleDebuggerPaused(event)
1637     {
1638         this.contentView.element.insertBefore(this._callStackSection.element, this.contentView.element.firstChild);
1639
1640         if (this._updatePauseReason())
1641             this.contentView.element.insertBefore(this._pauseReasonSection.element, this.contentView.element.firstChild);
1642
1643         this._debuggerPauseResumeButtonItem.enabled = true;
1644         this._debuggerPauseResumeButtonItem.toggled = true;
1645         this._debuggerStepOverButtonItem.enabled = true;
1646         this._debuggerStepIntoButtonItem.enabled = true;
1647         this._debuggerStepOutButtonItem.enabled = true;
1648
1649         this.element.classList.add("paused");
1650     }
1651
1652     _handleDebuggerResumed(event)
1653     {
1654         this._callStackSection.element.remove();
1655
1656         this._pauseReasonSection.element.remove();
1657
1658         this._debuggerPauseResumeButtonItem.enabled = true;
1659         this._debuggerPauseResumeButtonItem.toggled = false;
1660         this._debuggerStepOverButtonItem.enabled = false;
1661         this._debuggerStepIntoButtonItem.enabled = false;
1662         this._debuggerStepOutButtonItem.enabled = false;
1663
1664         this.element.classList.remove("paused");
1665     }
1666
1667     _handleDebuggerCallFramesDidChange(event)
1668     {
1669         let {target} = event.data;
1670         let treeElement = this._findCallStackTargetTreeElement(target);
1671         console.assert(treeElement);
1672         if (treeElement)
1673             treeElement.refresh();
1674     }
1675
1676     _handleDebuggerActiveCallFrameDidChange(event)
1677     {
1678         if (this._activeCallFrameTreeElement) {
1679             this._activeCallFrameTreeElement.isActiveCallFrame = false;
1680             this._activeCallFrameTreeElement = null;
1681         }
1682
1683         if (!WI.debuggerManager.activeCallFrame)
1684             return;
1685
1686         this._activeCallFrameTreeElement = this._callStackTreeOutline.findTreeElement(WI.debuggerManager.activeCallFrame);
1687         if (this._activeCallFrameTreeElement)
1688             this._activeCallFrameTreeElement.isActiveCallFrame = true;
1689     }
1690
1691     _handleDebuggerWaitingToPause(event)
1692     {
1693         this._debuggerPauseResumeButtonItem.enabled = false;
1694     }
1695
1696     _handleDebuggerObjectDisplayLocationDidChange(event)
1697     {
1698         let debuggerObject = event.target;
1699
1700         if (event.data.oldDisplaySourceCode === debuggerObject.sourceCodeLocation.displaySourceCode)
1701             return;
1702
1703         // A known debugger object (breakpoint, issueMessage, etc.) moved between resources. Remove
1704         // the old tree element and create a new tree element with the updated file.
1705
1706         let wasSelected = false;
1707         let oldDebuggerTreeElement = null;
1708         let newDebuggerTreeElement = null;
1709         if (debuggerObject instanceof WI.Breakpoint) {
1710             oldDebuggerTreeElement = this._breakpointsTreeOutline.findTreeElement(debuggerObject);
1711             if (oldDebuggerTreeElement) {
1712                 newDebuggerTreeElement = this._addBreakpoint(debuggerObject);
1713                 wasSelected = oldDebuggerTreeElement.selected;
1714             }
1715         } else if (debuggerObject instanceof WI.IssueMessage) {
1716             oldDebuggerTreeElement = this._resourcesTreeOutline.findTreeElement(debuggerObject);
1717             if (oldDebuggerTreeElement) {
1718                 newDebuggerTreeElement = this._addIssue(debuggerObject);
1719                 wasSelected = oldDebuggerTreeElement.selected;
1720             }
1721         }
1722
1723         if (oldDebuggerTreeElement)
1724             this._removeDebuggerTreeElement(oldDebuggerTreeElement);
1725
1726         if (newDebuggerTreeElement && wasSelected)
1727             newDebuggerTreeElement.revealAndSelect(true, false, true);
1728     }
1729
1730     _handleDOMBreakpointDOMNodeChanged(event)
1731     {
1732         let breakpoint = event.target;
1733         if (breakpoint.domNodeIdentifier)
1734             this._addBreakpoint(breakpoint);
1735         else
1736             this._removeBreakpoint(breakpoint);
1737     }
1738
1739     _handleConsoleIssueAdded(event)
1740     {
1741         let {issue} = event.data;
1742
1743         // We only want to show issues originating from JavaScript source code.
1744         if (!issue.sourceCodeLocation || !issue.sourceCodeLocation.sourceCode || (issue.source !== "javascript" && issue.source !== "console-api"))
1745             return;
1746
1747         this._addIssue(issue);
1748     }
1749
1750     _handleConsoleCleared(event)
1751     {
1752         let issueTreeElements = [];
1753         let currentTreeElement = this._resourcesTreeOutline.children[0];
1754         while (currentTreeElement && !currentTreeElement.root) {
1755             if (currentTreeElement instanceof WI.IssueTreeElement)
1756                 issueTreeElements.push(currentTreeElement);
1757
1758             const skipUnrevealed = false;
1759             const stayWithin = null;
1760             const dontPopulate = true;
1761             currentTreeElement = currentTreeElement.traverseNextTreeElement(skipUnrevealed, stayWithin, dontPopulate);
1762         }
1763
1764         issueTreeElements.forEach((treeElement) => treeElement.parent.removeChild(treeElement));
1765     }
1766
1767     _handleTimelineCapturingWillStart(event)
1768     {
1769         this._updateTemporarilyDisabledBreakpointsButtons();
1770
1771         if (!this._timelineRecordingWarningElement) {
1772             let stopRecordingButton = document.createElement("button");
1773             stopRecordingButton.textContent = WI.UIString("Stop recording");
1774             stopRecordingButton.addEventListener("click", () => {
1775                 WI.timelineManager.stopCapturing();
1776             });
1777
1778             this._timelineRecordingWarningElement = document.createElement("div");
1779             this._timelineRecordingWarningElement.classList.add("warning-banner");
1780             this._timelineRecordingWarningElement.append(WI.UIString("Debugger disabled during Timeline recording"), document.createElement("br"), stopRecordingButton);
1781         }
1782
1783         this.contentView.element.insertBefore(this._timelineRecordingWarningElement, this.contentView.element.firstChild);
1784
1785         this._updateBreakpointsDisabledBanner();
1786     }
1787
1788     _handleTimelineCapturingStopped(event)
1789     {
1790         this._updateTemporarilyDisabledBreakpointsButtons();
1791
1792         if (this._timelineRecordingWarningElement) {
1793             this._timelineRecordingWarningElement.remove();
1794             this._timelineRecordingWarningElement = null;
1795         }
1796
1797         this._updateBreakpointsDisabledBanner();
1798     }
1799
1800     _handleAuditManagerTestScheduled(event)
1801     {
1802         this._updateTemporarilyDisabledBreakpointsButtons();
1803
1804         if (!this._auditTestWarningElement) {
1805             let stopAuditButton = document.createElement("button");
1806             stopAuditButton.textContent = WI.UIString("Stop Audit");
1807             stopAuditButton.addEventListener("click", (event) => {
1808                 WI.auditManager.stop();
1809             });
1810
1811             this._auditTestWarningElement = document.createElement("div");
1812             this._auditTestWarningElement.classList.add("warning-banner");
1813             this._auditTestWarningElement.append(WI.UIString("Debugger disabled during Audit"), document.createElement("br"), stopAuditButton);
1814         }
1815
1816         this.contentView.element.insertBefore(this._auditTestWarningElement, this.contentView.element.firstChild);
1817
1818         this._updateBreakpointsDisabledBanner();
1819     }
1820
1821     _handleAuditManagerTestCompleted(event)
1822     {
1823         this._updateTemporarilyDisabledBreakpointsButtons();
1824
1825         if (this._auditTestWarningElement) {
1826             this._auditTestWarningElement.remove();
1827             this._auditTestWarningElement = null;
1828         }
1829
1830         this._updateBreakpointsDisabledBanner();
1831     }
1832
1833     _handleCSSStyleSheetAdded(event)
1834     {
1835         let styleSheet = event.data.styleSheet;
1836         if (!styleSheet.isInspectorStyleSheet())
1837             return;
1838
1839         let frameTreeElement = this.treeElementForRepresentedObject(styleSheet.parentFrame);
1840         if (!frameTreeElement)
1841             return;
1842
1843         frameTreeElement.addRepresentedObjectToNewChildQueue(styleSheet);
1844     }
1845
1846     _handleTargetAdded(event)
1847     {
1848         this._addTarget(event.data.target);
1849     }
1850
1851     _handleTargetRemoved(event)
1852     {
1853         let {target} = event.data;
1854
1855         let workerTreeElement = this._workerTargetTreeElementMap.take(target);
1856         if (workerTreeElement)
1857             workerTreeElement.parent.removeChild(workerTreeElement);
1858
1859         let callStackTreeElement = this._findCallStackTargetTreeElement(target);
1860         console.assert(callStackTreeElement);
1861         if (callStackTreeElement)
1862             this._callStackTreeOutline.removeChild(callStackTreeElement);
1863
1864         this._updateCallStackTreeOutline();
1865     }
1866
1867     _handleExtraDomainsActivated()
1868     {
1869         if (WI.SourcesNavigationSidebarPanel.shouldPlaceResourcesAtTopLevel())
1870             this._resourcesTreeOutline.disclosureButtons = true;
1871     }
1872 };
1873
1874 WI.SourcesNavigationSidebarPanel.ResourceTypeSymbol = Symbol("resource-type");