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