Web Inspector: Missing checks in DebuggerSidebarPanel for DOM debugging support
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / DebuggerSidebarPanel.js
1 /*
2  * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSidebarPanel
27 {
28     constructor(contentBrowser)
29     {
30         super("debugger", WI.UIString("Debugger"), true);
31
32         this.contentBrowser = contentBrowser;
33
34         WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
35         WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceAdded, this);
36         WI.Target.addEventListener(WI.Target.Event.ResourceAdded, this._resourceAdded, this);
37
38         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
39         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this);
40         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this);
41         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this);
42         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptRemoved, this._scriptRemoved, this);
43         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptsCleared, this._scriptsCleared, this);
44         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
45         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
46         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.CallFramesDidChange, this._debuggerCallFramesDidChange, this);
47         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this._debuggerActiveCallFrameDidChange, this);
48         WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.WaitingToPause, this._debuggerWaitingToPause, this);
49
50         WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingWillStart, this._timelineCapturingWillStart, this);
51         WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStopped, this._timelineCapturingStopped, this);
52
53         WI.targetManager.addEventListener(WI.TargetManager.Event.TargetAdded, this._targetAdded, this);
54         WI.targetManager.addEventListener(WI.TargetManager.Event.TargetRemoved, this._targetRemoved, this);
55
56         this._timelineRecordingWarningElement = document.createElement("div");
57         this._timelineRecordingWarningElement.classList.add("warning-banner");
58         this._timelineRecordingWarningElement.append(WI.UIString("Debugger disabled during Timeline recording"), " ");
59         let stopRecordingLink = this._timelineRecordingWarningElement.appendChild(document.createElement("a"));
60         stopRecordingLink.textContent = WI.UIString("Stop recording");
61         stopRecordingLink.addEventListener("click", () => { WI.timelineManager.stopCapturing(); });
62
63         this._breakpointsDisabledWarningElement = document.createElement("div");
64         this._breakpointsDisabledWarningElement.classList.add("warning-banner");
65         this._breakpointsDisabledWarningElement.append(WI.UIString("Breakpoints disabled"), document.createElement("br"));
66         let enableBreakpointsLink = this._breakpointsDisabledWarningElement.appendChild(document.createElement("a"));
67         enableBreakpointsLink.textContent = WI.UIString("Enable breakpoints");
68         enableBreakpointsLink.addEventListener("click", () => { WI.debuggerToggleBreakpoints(); });
69
70         this._navigationBar = new WI.NavigationBar;
71         this.addSubview(this._navigationBar);
72
73         var breakpointsImage = {src: "Images/Breakpoints.svg", width: 15, height: 15};
74         var pauseImage = {src: "Images/Pause.svg", width: 15, height: 15};
75         var resumeImage = {src: "Images/Resume.svg", width: 15, height: 15};
76         var stepOverImage = {src: "Images/StepOver.svg", width: 15, height: 15};
77         var stepIntoImage = {src: "Images/StepInto.svg", width: 15, height: 15};
78         var stepOutImage = {src: "Images/StepOut.svg", width: 15, height: 15};
79
80         var toolTip = WI.UIString("Enable all breakpoints (%s)").format(WI.toggleBreakpointsKeyboardShortcut.displayName);
81         var altToolTip = WI.UIString("Disable all breakpoints (%s)").format(WI.toggleBreakpointsKeyboardShortcut.displayName);
82
83         this._debuggerBreakpointsButtonItem = new WI.ActivateButtonNavigationItem("debugger-breakpoints", toolTip, altToolTip, breakpointsImage.src, breakpointsImage.width, breakpointsImage.height);
84         this._debuggerBreakpointsButtonItem.activated = WI.debuggerManager.breakpointsEnabled;
85         this._debuggerBreakpointsButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerToggleBreakpoints, this);
86         this._navigationBar.addNavigationItem(this._debuggerBreakpointsButtonItem);
87
88         toolTip = WI.UIString("Pause script execution (%s or %s)").format(WI.pauseOrResumeKeyboardShortcut.displayName, WI.pauseOrResumeAlternateKeyboardShortcut.displayName);
89         altToolTip = WI.UIString("Continue script execution (%s or %s)").format(WI.pauseOrResumeKeyboardShortcut.displayName, WI.pauseOrResumeAlternateKeyboardShortcut.displayName);
90
91         this._debuggerPauseResumeButtonItem = new WI.ToggleButtonNavigationItem("debugger-pause-resume", toolTip, altToolTip, pauseImage.src, resumeImage.src, pauseImage.width, pauseImage.height);
92         this._debuggerPauseResumeButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerPauseResumeToggle, this);
93         this._navigationBar.addNavigationItem(this._debuggerPauseResumeButtonItem);
94
95         this._debuggerStepOverButtonItem = new WI.ButtonNavigationItem("debugger-step-over", WI.UIString("Step over (%s or %s)").format(WI.stepOverKeyboardShortcut.displayName, WI.stepOverAlternateKeyboardShortcut.displayName), stepOverImage.src, stepOverImage.width, stepOverImage.height);
96         this._debuggerStepOverButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepOver, this);
97         this._debuggerStepOverButtonItem.enabled = false;
98         this._navigationBar.addNavigationItem(this._debuggerStepOverButtonItem);
99
100         this._debuggerStepIntoButtonItem = new WI.ButtonNavigationItem("debugger-step-into", WI.UIString("Step into (%s or %s)").format(WI.stepIntoKeyboardShortcut.displayName, WI.stepIntoAlternateKeyboardShortcut.displayName), stepIntoImage.src, stepIntoImage.width, stepIntoImage.height);
101         this._debuggerStepIntoButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepInto, this);
102         this._debuggerStepIntoButtonItem.enabled = false;
103         this._navigationBar.addNavigationItem(this._debuggerStepIntoButtonItem);
104
105         this._debuggerStepOutButtonItem = new WI.ButtonNavigationItem("debugger-step-out", WI.UIString("Step out (%s or %s)").format(WI.stepOutKeyboardShortcut.displayName, WI.stepOutAlternateKeyboardShortcut.displayName), stepOutImage.src, stepOutImage.width, stepOutImage.height);
106         this._debuggerStepOutButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepOut, this);
107         this._debuggerStepOutButtonItem.enabled = false;
108         this._navigationBar.addNavigationItem(this._debuggerStepOutButtonItem);
109
110         // Add this offset-sections class name so the sticky headers don't overlap the navigation bar.
111         this.element.classList.add(WI.DebuggerSidebarPanel.OffsetSectionsStyleClassName);
112
113         this._allExceptionsBreakpointTreeElement = new WI.BreakpointTreeElement(WI.debuggerManager.allExceptionsBreakpoint, WI.DebuggerSidebarPanel.ExceptionIconStyleClassName, WI.UIString("All Exceptions"));
114         this._allUncaughtExceptionsBreakpointTreeElement = new WI.BreakpointTreeElement(WI.debuggerManager.allUncaughtExceptionsBreakpoint, WI.DebuggerSidebarPanel.ExceptionIconStyleClassName, WI.UIString("Uncaught Exceptions"));
115         this._assertionsBreakpointTreeElement = new WI.BreakpointTreeElement(WI.debuggerManager.assertionsBreakpoint, WI.DebuggerSidebarPanel.AssertionIconStyleClassName, WI.UIString("Assertion Failures"));
116
117         this.suppressFilteringOnTreeElements([this._allExceptionsBreakpointTreeElement, this._allUncaughtExceptionsBreakpointTreeElement, this._assertionsBreakpointTreeElement]);
118
119         function showResourcesWithIssuesOnlyFilterFunction(treeElement)
120         {
121             // Issues are only shown in the scripts tree outline.
122             if (treeElement.treeOutline !== this._scriptsContentTreeOutline)
123                 return true;
124
125             // Keep issues.
126             if (treeElement instanceof WI.IssueTreeElement)
127                 return true;
128
129             // Keep resources with issues.
130             if (treeElement.hasChildren) {
131                 for (let child of treeElement.children) {
132                     if (child instanceof WI.IssueTreeElement)
133                         return true;
134                 }
135             }
136             return false;
137         }
138
139         this.filterBar.addFilterBarButton("debugger-show-resources-with-issues-only", showResourcesWithIssuesOnlyFilterFunction.bind(this), false, WI.UIString("Only show resources with issues"), WI.UIString("Show all resources"), "Images/Errors.svg", 15, 15);
140
141         this._breakpointsContentTreeOutline = this.contentTreeOutline;
142
143         let breakpointsRow = new WI.DetailsSectionRow;
144         breakpointsRow.element.appendChild(this._breakpointsContentTreeOutline.element);
145
146         let breakpointsGroup = new WI.DetailsSectionGroup([breakpointsRow]);
147         let breakpointsSection = new WI.DetailsSection("breakpoints", WI.UIString("Breakpoints"), [breakpointsGroup]);
148         this.contentView.element.appendChild(breakpointsSection.element);
149
150         this._breakpointSectionElement = breakpointsSection.element;
151
152         this._breakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
153         this._breakpointsContentTreeOutline.ondelete = this._breakpointTreeOutlineDeleteTreeElement.bind(this);
154         this._breakpointsContentTreeOutline.populateContextMenu = function(contextMenu, event, treeElement) {
155             this._breakpointTreeOutlineContextMenuTreeElement(contextMenu, event, treeElement);
156
157             WI.TreeOutline.prototype.populateContextMenu(contextMenu, event, treeElement);
158         }.bind(this);
159
160         this._breakpointsContentTreeOutline.appendChild(this._allExceptionsBreakpointTreeElement);
161         this._breakpointsContentTreeOutline.appendChild(this._allUncaughtExceptionsBreakpointTreeElement);
162
163         // COMPATIBILITY (iOS 10): DebuggerAgent.setPauseOnAssertions did not exist yet.
164         if (DebuggerAgent.setPauseOnAssertions)
165             this._breakpointsContentTreeOutline.appendChild(this._assertionsBreakpointTreeElement);
166
167         if (WI.domDebuggerManager.supported) {
168             this._domBreakpointsContentTreeOutline = this.createContentTreeOutline(true);
169             this._domBreakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.ElementAdded, this._domBreakpointAddedOrRemoved, this);
170             this._domBreakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.ElementRemoved, this._domBreakpointAddedOrRemoved, this);
171             this._domBreakpointTreeController = new WI.DOMBreakpointTreeController(this._domBreakpointsContentTreeOutline);
172
173             this._domBreakpointsRow = new WI.DetailsSectionRow(WI.UIString("No Breakpoints"));
174             this._domBreakpointsRow.element.appendChild(this._domBreakpointsContentTreeOutline.element);
175             this._domBreakpointsRow.showEmptyMessage();
176
177             const defaultCollapsed = true;
178
179             let domBreakpointsGroup = new WI.DetailsSectionGroup([this._domBreakpointsRow]);
180             this._domBreakpointsSection = new WI.DetailsSection("dom-breakpoints", WI.UIString("DOM Breakpoints"), [domBreakpointsGroup], null, defaultCollapsed);
181             this.contentView.element.appendChild(this._domBreakpointsSection.element);
182
183             this._xhrBreakpointsContentTreeOutline = this.createContentTreeOutline(true);
184             this._xhrBreakpointTreeController = new WI.XHRBreakpointTreeController(this._xhrBreakpointsContentTreeOutline);
185
186             this._xhrBreakpointsRow = new WI.DetailsSectionRow;
187             this._xhrBreakpointsRow.element.appendChild(this._xhrBreakpointsContentTreeOutline.element);
188
189             let navigationBar = new WI.NavigationBar;
190             let navigationBarWrapper = document.createElement("div");
191             navigationBarWrapper.appendChild(navigationBar.element);
192
193             let addXHRBreakpointButton = new WI.ButtonNavigationItem("add-xhr-breakpoint", WI.UIString("Add XHR Breakpoint"), "Images/Plus13.svg", 13, 13);
194             addXHRBreakpointButton.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._addXHRBreakpointButtonClicked, this);
195             navigationBar.addNavigationItem(addXHRBreakpointButton);
196
197             let xhrBreakpointsGroup = new WI.DetailsSectionGroup([this._xhrBreakpointsRow]);
198             let xhrBreakpointsSection = new WI.DetailsSection("xhr-breakpoints", WI.UIString("XHR Breakpoints"), [xhrBreakpointsGroup], navigationBarWrapper, defaultCollapsed);
199             this.contentView.element.appendChild(xhrBreakpointsSection.element);
200         }
201
202         this._scriptsContentTreeOutline = this.createContentTreeOutline();
203         this._scriptsContentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
204         this._scriptsContentTreeOutline.includeSourceMapResourceChildren = true;
205
206         let scriptsRow = new WI.DetailsSectionRow;
207         scriptsRow.element.appendChild(this._scriptsContentTreeOutline.element);
208
209         let scriptsGroup = new WI.DetailsSectionGroup([scriptsRow]);
210         this._scriptsSection = new WI.DetailsSection("scripts", WI.UIString("Sources"), [scriptsGroup]);
211         this.contentView.element.appendChild(this._scriptsSection.element);
212
213         const suppressFiltering = true;
214         this._callStackTreeOutline = this.createContentTreeOutline(suppressFiltering);
215         this._callStackTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
216
217         this._mainTargetTreeElement = new WI.ThreadTreeElement(WI.mainTarget);
218         this._callStackTreeOutline.appendChild(this._mainTargetTreeElement);
219
220         this._updateCallStackTreeOutline();
221
222         this._callStackRow = new WI.DetailsSectionRow;
223         this._callStackRow.element.appendChild(this._callStackTreeOutline.element);
224
225         this._callStackGroup = new WI.DetailsSectionGroup([this._callStackRow]);
226         this._callStackSection = new WI.DetailsSection("call-stack", WI.UIString("Call Stack"), [this._callStackGroup]);
227
228         this._showingSingleThreadCallStack = true;
229
230         this._activeCallFrameTreeElement = null;
231
232         this._pauseReasonTreeOutline = null;
233
234         this._pauseReasonLinkContainerElement = document.createElement("span");
235         this._pauseReasonTextRow = new WI.DetailsSectionTextRow;
236         this._pauseReasonGroup = new WI.DetailsSectionGroup([this._pauseReasonTextRow]);
237         this._pauseReasonSection = new WI.DetailsSection("paused-reason", WI.UIString("Pause Reason"), [this._pauseReasonGroup], this._pauseReasonLinkContainerElement);
238
239         WI.Breakpoint.addEventListener(WI.Breakpoint.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
240         WI.IssueMessage.addEventListener(WI.IssueMessage.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
241         WI.issueManager.addEventListener(WI.IssueManager.Event.IssueWasAdded, this._handleIssueAdded, this);
242         WI.issueManager.addEventListener(WI.IssueManager.Event.Cleared, this._handleIssuesCleared, this);
243
244         if (WI.frameResourceManager.mainFrame)
245             this._addResourcesRecursivelyForFrame(WI.frameResourceManager.mainFrame);
246
247         for (var script of WI.debuggerManager.knownNonResourceScripts)
248             this._addScript(script);
249
250         if (WI.debuggerManager.paused)
251             this._debuggerDidPause(null);
252
253         if (WI.timelineManager.isCapturing() && WI.debuggerManager.breakpointsDisabledTemporarily)
254             this._timelineCapturingWillStart(null);
255
256         this._updateBreakpointsDisabledBanner();
257     }
258
259     // Public
260
261     get minimumWidth()
262     {
263         return this._navigationBar.minimumWidth;
264     }
265
266     closed()
267     {
268         super.closed();
269
270         if (this._domBreakpointTreeController) {
271             this._domBreakpointTreeController.disconnect();
272             this._domBreakpointTreeController = null;
273         }
274
275         WI.Frame.removeEventListener(null, null, this);
276         WI.debuggerManager.removeEventListener(null, null, this);
277         WI.Breakpoint.removeEventListener(null, null, this);
278         WI.IssueMessage.removeEventListener(null, null, this);
279     }
280
281     showDefaultContentView()
282     {
283         if (WI.frameResourceManager.mainFrame) {
284             let mainTreeElement = this._scriptsContentTreeOutline.findTreeElement(WI.frameResourceManager.mainFrame.mainResource);
285             if (mainTreeElement && this.showDefaultContentViewForTreeElement(mainTreeElement))
286                 return;
287         }
288
289         let currentTreeElement = this._scriptsContentTreeOutline.children[0];
290         while (currentTreeElement && !currentTreeElement.root) {
291             if (currentTreeElement instanceof WI.ResourceTreeElement || currentTreeElement instanceof WI.ScriptTreeElement) {
292                 if (this.showDefaultContentViewForTreeElement(currentTreeElement))
293                     return;
294             }
295
296             currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true);
297         }
298     }
299
300     treeElementForRepresentedObject(representedObject)
301     {
302         // The main resource is used as the representedObject instead of Frame in our tree.
303         if (representedObject instanceof WI.Frame)
304             representedObject = representedObject.mainResource;
305
306         let treeElement = this._breakpointsContentTreeOutline.findTreeElement(representedObject);
307         if (!treeElement)
308             treeElement = this._scriptsContentTreeOutline.findTreeElement(representedObject);
309
310         if (treeElement)
311             return treeElement;
312
313         // Only special case Script objects.
314         if (!(representedObject instanceof WI.Script)) {
315             console.error("Didn't find a TreeElement for representedObject", representedObject);
316             return null;
317         }
318
319         // If the Script has a URL we should have found it earlier.
320         if (representedObject.url) {
321             console.error("Didn't find a ScriptTreeElement for a Script with a URL.");
322             return null;
323         }
324
325         // Since the Script does not have a URL we consider it an 'anonymous' script. These scripts happen from calls to
326         // window.eval() or browser features like Auto Fill and Reader. They are not normally added to the sidebar, but since
327         // we have a ScriptContentView asking for the tree element we will make a ScriptTreeElement on demand and add it.
328
329         return this._addScript(representedObject);
330     }
331
332     // Protected
333
334     saveStateToCookie(cookie)
335     {
336         console.assert(cookie);
337
338         var selectedTreeElement = this._breakpointsContentTreeOutline.selectedTreeElement;
339         if (!selectedTreeElement) {
340             super.saveStateToCookie(cookie);
341             return;
342         }
343
344         var representedObject = selectedTreeElement.representedObject;
345
346         if (representedObject === WI.debuggerManager.allExceptionsBreakpoint) {
347             cookie[WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey] = true;
348             return;
349         }
350
351         if (representedObject === WI.debuggerManager.allUncaughtExceptionsBreakpoint) {
352             cookie[WI.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey] = true;
353             return;
354         }
355
356         if (representedObject === WI.debuggerManager.assertionsBreakpoint) {
357             cookie[WI.DebuggerSidebarPanel.SelectedAssertionsCookieKey] = true;
358             return;
359         }
360
361         if (representedObject === WI.domDebuggerManager.allRequestsBreakpoint) {
362             cookie[WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey] = true;
363             return;
364         }
365
366         super.saveStateToCookie(cookie);
367     }
368
369     restoreStateFromCookie(cookie, relaxedMatchDelay)
370     {
371         console.assert(cookie);
372
373         // Eagerly resolve the special breakpoints; otherwise, use the default behavior.
374         if (cookie[WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey])
375             this._allExceptionsBreakpointTreeElement.revealAndSelect();
376         else if (cookie[WI.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey])
377             this._allUncaughtExceptionsBreakpointTreeElement.revealAndSelect();
378         else if (cookie[WI.DebuggerSidebarPanel.SelectedAssertionsCookieKey])
379             this._assertionsBreakpointTreeElement.revealAndSelect();
380         else if (cookie[WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey])
381             if (this._xhrBreakpointTreeController)
382                 this._xhrBreakpointTreeController.revealAndSelect(WI.domDebuggerManager.allRequestsBreakpoint);
383         else
384             super.restoreStateFromCookie(cookie, relaxedMatchDelay);
385     }
386
387     // Private
388
389     _debuggerWaitingToPause(event)
390     {
391         this._debuggerPauseResumeButtonItem.enabled = false;
392     }
393
394     _debuggerDidPause(event)
395     {
396         this.contentView.element.insertBefore(this._callStackSection.element, this.contentView.element.firstChild);
397
398         if (this._updatePauseReason())
399             this.contentView.element.insertBefore(this._pauseReasonSection.element, this.contentView.element.firstChild);
400
401         this._debuggerPauseResumeButtonItem.enabled = true;
402         this._debuggerPauseResumeButtonItem.toggled = true;
403         this._debuggerStepOverButtonItem.enabled = true;
404         this._debuggerStepIntoButtonItem.enabled = true;
405         this._debuggerStepOutButtonItem.enabled = true;
406
407         this.element.classList.add(WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName);
408     }
409
410     _debuggerDidResume(event)
411     {
412         this._callStackSection.element.remove();
413
414         this._pauseReasonSection.element.remove();
415
416         this._debuggerPauseResumeButtonItem.enabled = true;
417         this._debuggerPauseResumeButtonItem.toggled = false;
418         this._debuggerStepOverButtonItem.enabled = false;
419         this._debuggerStepIntoButtonItem.enabled = false;
420         this._debuggerStepOutButtonItem.enabled = false;
421
422         this.element.classList.remove(WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName);
423     }
424
425     _breakpointsEnabledDidChange(event)
426     {
427         this._debuggerBreakpointsButtonItem.activated = WI.debuggerManager.breakpointsEnabled;
428
429         this._updateBreakpointsDisabledBanner();
430     }
431
432     _addBreakpoint(breakpoint)
433     {
434         let sourceCode = breakpoint.sourceCodeLocation.displaySourceCode;
435         if (!sourceCode)
436             return null;
437
438         if (this._breakpointsContentTreeOutline.findTreeElement(breakpoint))
439             return;
440
441         let parentTreeElement = this._addTreeElementForSourceCodeToTreeOutline(sourceCode, this._breakpointsContentTreeOutline);
442
443         // Mark disabled breakpoints as resolved if there is source code loaded with that URL.
444         // This gives the illusion the breakpoint was resolved, but since we don't send disabled
445         // breakpoints to the backend we don't know for sure. If the user enables the breakpoint
446         // it will be resolved properly.
447         if (breakpoint.disabled)
448             breakpoint.resolved = true;
449
450         let breakpointTreeElement = new WI.BreakpointTreeElement(breakpoint);
451         parentTreeElement.insertChild(breakpointTreeElement, insertionIndexForObjectInListSortedByFunction(breakpointTreeElement, parentTreeElement.children, this._compareTreeElements));
452         if (parentTreeElement.children.length === 1)
453             parentTreeElement.expand();
454         return breakpointTreeElement;
455     }
456
457     _addBreakpointsForSourceCode(sourceCode)
458     {
459         var breakpoints = WI.debuggerManager.breakpointsForSourceCode(sourceCode);
460         for (var i = 0; i < breakpoints.length; ++i)
461             this._addBreakpoint(breakpoints[i], sourceCode);
462     }
463
464     _addIssuesForSourceCode(sourceCode)
465     {
466         var issues = WI.issueManager.issuesForSourceCode(sourceCode);
467         for (var issue of issues)
468             this._addIssue(issue);
469     }
470
471     _addTreeElementForSourceCodeToTreeOutline(sourceCode, treeOutline)
472     {
473         let treeElement = treeOutline.getCachedTreeElement(sourceCode);
474         if (!treeElement) {
475             if (sourceCode instanceof WI.SourceMapResource)
476                 treeElement = new WI.SourceMapResourceTreeElement(sourceCode);
477             else if (sourceCode instanceof WI.Resource)
478                 treeElement = new WI.ResourceTreeElement(sourceCode);
479             else if (sourceCode instanceof WI.Script)
480                 treeElement = new WI.ScriptTreeElement(sourceCode);
481         }
482
483         if (!treeElement) {
484             console.error("Unknown sourceCode instance", sourceCode);
485             return null;
486         }
487
488         if (!treeElement.parent) {
489             treeElement.hasChildren = false;
490             treeElement.expand();
491
492             treeOutline.insertChild(treeElement, insertionIndexForObjectInListSortedByFunction(treeElement, treeOutline.children, this._compareTopLevelTreeElements.bind(this)));
493         }
494
495         return treeElement;
496     }
497
498     _addResourcesRecursivelyForFrame(frame)
499     {
500         this._addResource(frame.mainResource);
501
502         for (let resource of frame.resourceCollection.items)
503             this._addResource(resource);
504
505         for (let childFrame of frame.childFrameCollection.items)
506             this._addResourcesRecursivelyForFrame(childFrame);
507     }
508
509     _resourceAdded(event)
510     {
511         this._addResource(event.data.resource);
512     }
513
514     _addResource(resource)
515     {
516         if (![WI.Resource.Type.Document, WI.Resource.Type.Script].includes(resource.type))
517             return;
518
519         let treeElement = this._addTreeElementForSourceCodeToTreeOutline(resource, this._scriptsContentTreeOutline);
520         this._addBreakpointsForSourceCode(resource);
521         this._addIssuesForSourceCode(resource);
522
523         if (this.parentSidebar && !this.contentBrowser.currentContentView)
524             this.showDefaultContentViewForTreeElement(treeElement);
525     }
526
527     _mainResourceDidChange(event)
528     {
529         if (event.target.isMainFrame()) {
530             // Aggressively prune resources now so the old resources are removed before
531             // the new main resource is added below. This avoids a visual flash when the
532             // prune normally happens on a later event loop cycle.
533             this.pruneStaleResourceTreeElements();
534             this.contentBrowser.contentViewContainer.closeAllContentViews();
535         }
536
537         if (!event.data.oldMainResource) {
538             var resource = event.target.mainResource;
539             this._addTreeElementForSourceCodeToTreeOutline(resource, this._scriptsContentTreeOutline);
540             this._addBreakpointsForSourceCode(resource);
541             this._addIssuesForSourceCode(resource);
542         }
543     }
544
545     _timelineCapturingWillStart(event)
546     {
547         this._debuggerBreakpointsButtonItem.enabled = false;
548         this._debuggerPauseResumeButtonItem.enabled = false;
549
550         this.contentView.element.insertBefore(this._timelineRecordingWarningElement, this.contentView.element.firstChild);
551         this._updateBreakpointsDisabledBanner();
552     }
553
554     _timelineCapturingStopped(event)
555     {
556         this._debuggerBreakpointsButtonItem.enabled = true;
557         this._debuggerPauseResumeButtonItem.enabled = true;
558
559         this._timelineRecordingWarningElement.remove();
560         this._updateBreakpointsDisabledBanner();
561     }
562
563     _updateBreakpointsDisabledBanner()
564     {
565         let breakpointsEnabled = WI.debuggerManager.breakpointsEnabled;
566         let timelineWarningShowing = !!this._timelineRecordingWarningElement.parentElement;
567
568         if (!breakpointsEnabled && !timelineWarningShowing)
569             this.contentView.element.insertBefore(this._breakpointsDisabledWarningElement, this.contentView.element.firstChild);
570         else
571             this._breakpointsDisabledWarningElement.remove();
572     }
573
574     _scriptAdded(event)
575     {
576         this._addScript(event.data.script);
577     }
578
579     _addScript(script)
580     {
581         // COMPATIBILITY(iOS 9): Backends could send the frontend built-in code, filter out JSC internals.
582         if (!script.url && !script.sourceURL)
583             return null;
584
585         // In general, do not show dynamically added script elements.
586         if (script.dynamicallyAddedScriptElement && !script.sourceURL)
587             return null;
588
589         // Don't add breakpoints if the script is represented by a Resource. They were
590         // already added by _resourceAdded.
591         if (script.resource)
592             return null;
593
594         let treeElement = this._addTreeElementForSourceCodeToTreeOutline(script, this._scriptsContentTreeOutline);
595         this._addBreakpointsForSourceCode(script);
596         this._addIssuesForSourceCode(script);
597
598         if (this.parentSidebar && !this.contentBrowser.currentContentView)
599             this.showDefaultContentViewForTreeElement(treeElement);
600
601         return treeElement;
602     }
603
604     _scriptRemoved(event)
605     {
606         function removeScript(script, treeOutline)
607         {
608             let scriptTreeElement = treeOutline.getCachedTreeElement(script);
609             if (scriptTreeElement)
610                 scriptTreeElement.parent.removeChild(scriptTreeElement);
611         }
612
613         let script = event.data.script;
614         removeScript(script, this._breakpointsContentTreeOutline);
615         removeScript(script, this._scriptsContentTreeOutline);
616     }
617
618     _scriptsCleared(event)
619     {
620         for (var i = this._breakpointsContentTreeOutline.children.length - 1; i >= 0; --i) {
621             var treeElement = this._breakpointsContentTreeOutline.children[i];
622             if (!(treeElement instanceof WI.ScriptTreeElement))
623                 continue;
624
625             this._breakpointsContentTreeOutline.removeChildAtIndex(i, true, true);
626         }
627
628         this._scriptsContentTreeOutline.removeChildren();
629
630         this._addResourcesRecursivelyForFrame(WI.frameResourceManager.mainFrame);
631     }
632
633     _breakpointAdded(event)
634     {
635         var breakpoint = event.data.breakpoint;
636         this._addBreakpoint(breakpoint);
637     }
638
639     _breakpointRemoved(event)
640     {
641         var breakpoint = event.data.breakpoint;
642
643         if (this._pauseReasonTreeOutline) {
644             var pauseReasonBreakpointTreeElement = this._pauseReasonTreeOutline.getCachedTreeElement(breakpoint);
645             if (pauseReasonBreakpointTreeElement)
646                 pauseReasonBreakpointTreeElement.removeStatusImage();
647         }
648
649         var breakpointTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(breakpoint);
650         console.assert(breakpointTreeElement);
651         if (!breakpointTreeElement)
652             return;
653
654         this._removeDebuggerTreeElement(breakpointTreeElement);
655     }
656
657     _findThreadTreeElementForTarget(target)
658     {
659         for (let child of this._callStackTreeOutline.children) {
660             if (child.target === target)
661                 return child;
662         }
663
664         return null;
665     }
666
667     _targetAdded(event)
668     {
669         let target = event.data.target;
670         let treeElement = new WI.ThreadTreeElement(target);
671         this._callStackTreeOutline.appendChild(treeElement);
672
673         this._updateCallStackTreeOutline();
674     }
675
676     _targetRemoved(event)
677     {
678         let target = event.data.target;
679         let treeElement = this._findThreadTreeElementForTarget(target);
680         this._callStackTreeOutline.removeChild(treeElement);
681
682         this._updateCallStackTreeOutline();
683     }
684
685     _updateCallStackTreeOutline()
686     {
687         let singleThreadShowing = WI.targets.size === 1;
688         this._callStackTreeOutline.element.classList.toggle("single-thread", singleThreadShowing);
689         this._mainTargetTreeElement.selectable = !singleThreadShowing;
690     }
691
692     _handleDebuggerObjectDisplayLocationDidChange(event)
693     {
694         var debuggerObject = event.target;
695
696         if (event.data.oldDisplaySourceCode === debuggerObject.sourceCodeLocation.displaySourceCode)
697             return;
698
699         var debuggerTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(debuggerObject);
700         if (!debuggerTreeElement)
701             return;
702
703         // A known debugger object (breakpoint, issueMessage, etc.) moved between resources, remove the old tree element
704         // and create a new tree element with the updated file.
705
706         var wasSelected = debuggerTreeElement.selected;
707
708         this._removeDebuggerTreeElement(debuggerTreeElement);
709         var newDebuggerTreeElement = this._addDebuggerObject(debuggerObject);
710
711         if (newDebuggerTreeElement && wasSelected)
712             newDebuggerTreeElement.revealAndSelect(true, false, true, true);
713     }
714
715     _removeDebuggerTreeElement(debuggerTreeElement)
716     {
717         // If this is a BreakpointTreeElement being deleted because of a cause
718         // outside of the TreeOutline then deselect if it is selected to avoid
719         // TreeOutline selection changes causing unexpected ContentView changes.
720         if (!debuggerTreeElement.__deletedViaDeleteKeyboardShortcut)
721             debuggerTreeElement.deselect();
722
723         let parentTreeElement = debuggerTreeElement.parent;
724         parentTreeElement.removeChild(debuggerTreeElement);
725
726         console.assert(parentTreeElement.parent === this._breakpointsContentTreeOutline);
727         if (parentTreeElement.children.length)
728             return;
729
730         parentTreeElement.treeOutline.removeChild(parentTreeElement);
731     }
732
733     _debuggerCallFramesDidChange(event)
734     {
735         let target = event.data.target;
736         let treeElement = this._findThreadTreeElementForTarget(target);
737         treeElement.refresh();
738     }
739
740     _debuggerActiveCallFrameDidChange()
741     {
742         if (this._activeCallFrameTreeElement) {
743             this._activeCallFrameTreeElement.isActiveCallFrame = false;
744             this._activeCallFrameTreeElement = null;
745         }
746
747         if (!WI.debuggerManager.activeCallFrame)
748             return;
749
750         this._activeCallFrameTreeElement = this._callStackTreeOutline.findTreeElement(WI.debuggerManager.activeCallFrame);
751         if (this._activeCallFrameTreeElement)
752             this._activeCallFrameTreeElement.isActiveCallFrame = true;
753     }
754
755     _breakpointsBeneathTreeElement(treeElement)
756     {
757         console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement);
758         if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
759             return [];
760
761         var breakpoints = [];
762         var breakpointTreeElements = treeElement.children;
763         for (var i = 0; i < breakpointTreeElements.length; ++i) {
764             console.assert(breakpointTreeElements[i] instanceof WI.BreakpointTreeElement);
765             console.assert(breakpointTreeElements[i].breakpoint);
766             var breakpoint = breakpointTreeElements[i].breakpoint;
767             if (breakpoint)
768                 breakpoints.push(breakpoint);
769         }
770
771         return breakpoints;
772     }
773
774     _removeAllBreakpoints(breakpoints)
775     {
776         for (var i = 0; i < breakpoints.length; ++i) {
777             var breakpoint = breakpoints[i];
778             if (WI.debuggerManager.isBreakpointRemovable(breakpoint))
779                 WI.debuggerManager.removeBreakpoint(breakpoint);
780         }
781     }
782
783     _toggleAllBreakpoints(breakpoints, disabled)
784     {
785         for (var i = 0; i < breakpoints.length; ++i)
786             breakpoints[i].disabled = disabled;
787     }
788
789     _breakpointTreeOutlineDeleteTreeElement(treeElement)
790     {
791         console.assert(treeElement.selected);
792         console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement);
793         if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
794             return false;
795
796         var wasTopResourceTreeElement = treeElement.previousSibling === this._assertionsBreakpointTreeElement || treeElement.previousSibling === this._allUncaughtExceptionsBreakpointTreeElement;
797         var nextSibling = treeElement.nextSibling;
798
799         var breakpoints = this._breakpointsBeneathTreeElement(treeElement);
800         this._removeAllBreakpoints(breakpoints);
801
802         if (wasTopResourceTreeElement && nextSibling)
803             nextSibling.select(true, true);
804
805         return true;
806     }
807
808     _breakpointTreeOutlineContextMenuTreeElement(contextMenu, event, treeElement)
809     {
810         // This check is necessary since the context menu is created by the TreeOutline, meaning
811         // that any child could be the target of the context menu event.
812         if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
813             return;
814
815         let breakpoints = this._breakpointsBeneathTreeElement(treeElement);
816         let shouldDisable = breakpoints.some((breakpoint) => !breakpoint.disabled);
817
818         let removeAllResourceBreakpoints = () => {
819             this._removeAllBreakpoints(breakpoints);
820         };
821
822         let toggleAllResourceBreakpoints = () => {
823             this._toggleAllBreakpoints(breakpoints, shouldDisable);
824         };
825
826         if (shouldDisable)
827             contextMenu.appendItem(WI.UIString("Disable Breakpoints"), toggleAllResourceBreakpoints);
828         else
829             contextMenu.appendItem(WI.UIString("Enable Breakpoints"), toggleAllResourceBreakpoints);
830         contextMenu.appendItem(WI.UIString("Delete Breakpoints"), removeAllResourceBreakpoints);
831     }
832
833     _treeSelectionDidChange(event)
834     {
835         if (!this.visible)
836             return;
837
838         let treeElement = event.data.selectedElement;
839         if (!treeElement)
840             return;
841
842         const options = {
843             ignoreNetworkTab: true,
844             ignoreSearchTab: true,
845         };
846
847         if (treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement) {
848             WI.showSourceCode(treeElement.representedObject, options);
849             return;
850         }
851
852         if (treeElement instanceof WI.CallFrameTreeElement) {
853             let callFrame = treeElement.callFrame;
854             if (callFrame.id)
855                 WI.debuggerManager.activeCallFrame = callFrame;
856
857             if (callFrame.sourceCodeLocation)
858                 WI.showSourceCodeLocation(callFrame.sourceCodeLocation, options);
859
860             return;
861         }
862
863         if (treeElement instanceof WI.IssueTreeElement) {
864             WI.showSourceCodeLocation(treeElement.issueMessage.sourceCodeLocation, options);
865             return;
866         }
867
868         if (!(treeElement instanceof WI.BreakpointTreeElement))
869             return;
870
871         let breakpoint = treeElement.breakpoint;
872         if (treeElement.treeOutline === this._pauseReasonTreeOutline) {
873             WI.showSourceCodeLocation(breakpoint.sourceCodeLocation, options);
874             return;
875         }
876
877         if (!treeElement.parent.representedObject)
878             return;
879
880         console.assert(treeElement.parent.representedObject instanceof WI.SourceCode);
881         if (!(treeElement.parent.representedObject instanceof WI.SourceCode))
882             return;
883
884         WI.showSourceCodeLocation(breakpoint.sourceCodeLocation, options);
885     }
886
887     _compareTopLevelTreeElements(a, b)
888     {
889         function isSpecialBreakpoint(treeElement)
890         {
891             return treeElement.representedObject === WI.debuggerManager.allExceptionsBreakpoint
892                 || treeElement.representedObject === WI.debuggerManager.allUncaughtExceptionsBreakpoint
893                 || treeElement.representedObject === WI.debuggerManager.assertionsBreakpoint;
894         }
895
896         if (isSpecialBreakpoint(a))
897             return -1;
898         if (isSpecialBreakpoint(b))
899             return 1;
900
901         return a.mainTitle.extendedLocaleCompare(b.mainTitle);
902     }
903
904     _compareTreeElements(a, b)
905     {
906         if (!a.representedObject || !b.representedObject)
907             return 0;
908
909         let aLocation = a.representedObject.sourceCodeLocation;
910         let bLocation = b.representedObject.sourceCodeLocation;
911         if (!aLocation || !bLocation)
912             return 0;
913
914         var comparisonResult = aLocation.displayLineNumber - bLocation.displayLineNumber;
915         if (comparisonResult !== 0)
916             return comparisonResult;
917
918         return aLocation.displayColumnNumber - bLocation.displayColumnNumber;
919     }
920
921     _updatePauseReason()
922     {
923         this._pauseReasonTreeOutline = null;
924
925         this._updatePauseReasonGotoArrow();
926         return this._updatePauseReasonSection();
927     }
928
929     _updatePauseReasonSection()
930     {
931         let target = WI.debuggerManager.activeCallFrame.target;
932         let targetData = WI.debuggerManager.dataForTarget(target);
933         let {pauseReason, pauseData} = targetData;
934
935         switch (pauseReason) {
936         case WI.DebuggerManager.PauseReason.Assertion:
937             // FIXME: We should include the assertion condition string.
938             console.assert(pauseData, "Expected data with an assertion, but found none.");
939             if (pauseData && pauseData.message) {
940                 this._pauseReasonTextRow.text = WI.UIString("Assertion with message: %s").format(pauseData.message);
941                 return true;
942             }
943
944             this._pauseReasonTextRow.text = WI.UIString("Assertion Failed");
945             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
946             return true;
947
948         case WI.DebuggerManager.PauseReason.Breakpoint:
949             console.assert(pauseData, "Expected breakpoint identifier, but found none.");
950             if (pauseData && pauseData.breakpointId) {
951                 const suppressFiltering = true;
952                 this._pauseReasonTreeOutline = this.createContentTreeOutline(suppressFiltering);
953                 this._pauseReasonTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
954
955                 let breakpoint = WI.debuggerManager.breakpointForIdentifier(pauseData.breakpointId);
956                 let breakpointTreeElement = new WI.BreakpointTreeElement(breakpoint, WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, WI.UIString("Triggered Breakpoint"));
957                 let breakpointDetailsSection = new WI.DetailsSectionRow;
958                 this._pauseReasonTreeOutline.appendChild(breakpointTreeElement);
959                 breakpointDetailsSection.element.appendChild(this._pauseReasonTreeOutline.element);
960
961                 this._pauseReasonGroup.rows = [breakpointDetailsSection];
962                 return true;
963             }
964             break;
965
966         case WI.DebuggerManager.PauseReason.CSPViolation:
967             console.assert(pauseData, "Expected data with a CSP Violation, but found none.");
968             if (pauseData) {
969                 // COMPATIBILITY (iOS 8): 'directive' was 'directiveText'.
970                 this._pauseReasonTextRow.text = WI.UIString("Content Security Policy violation of directive: %s").format(pauseData.directive || pauseData.directiveText);
971                 this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
972                 return true;
973             }
974             break;
975
976         case WI.DebuggerManager.PauseReason.DebuggerStatement:
977             this._pauseReasonTextRow.text = WI.UIString("Debugger Statement");
978             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
979             return true;
980
981         case WI.DebuggerManager.PauseReason.DOM:
982             console.assert(WI.domDebuggerManager.supported);
983             console.assert(pauseData, "Expected DOM breakpoint data, but found none.");
984             if (pauseData && pauseData.nodeId) {
985                 let domNode = WI.domTreeManager.nodeForId(pauseData.nodeId);
986                 let domBreakpoints = WI.domDebuggerManager.domBreakpointsForNode(domNode);
987                 let domBreakpoint;
988                 for (let breakpoint of domBreakpoints) {
989                     if (breakpoint.type === pauseData.type) {
990                         domBreakpoint = breakpoint;
991                         break;
992                     }
993                 }
994
995                 if (!domBreakpoint)
996                     return;
997
998                 const suppressFiltering = true;
999                 this._pauseReasonTreeOutline = this.createContentTreeOutline(suppressFiltering);
1000
1001                 let type = WI.DOMBreakpointTreeElement.displayNameForType(domBreakpoint.type);
1002                 let domBreakpointTreeElement = new WI.DOMBreakpointTreeElement(domBreakpoint, WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, type);
1003                 let domBreakpointRow = new WI.DetailsSectionRow;
1004                 this._pauseReasonTreeOutline.appendChild(domBreakpointTreeElement);
1005                 domBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1006
1007                 let ownerElementRow = new WI.DetailsSectionSimpleRow(WI.UIString("Element"), WI.linkifyNodeReference(domNode));
1008                 this._pauseReasonGroup.rows = [domBreakpointRow, ownerElementRow];
1009
1010                 if (domBreakpoint.type !== WI.DOMBreakpoint.Type.SubtreeModified)
1011                     return true;
1012
1013                 console.assert(pauseData.targetNode);
1014
1015                 let remoteObject = WI.RemoteObject.fromPayload(pauseData.targetNode, target);
1016                 remoteObject.pushNodeToFrontend((nodeId) => {
1017                     if (!nodeId)
1018                         return;
1019
1020                     let node = WI.domTreeManager.nodeForId(nodeId);
1021                     console.assert(node, "Missing node for id.", nodeId);
1022                     if (!node)
1023                         return;
1024
1025                     let fragment = document.createDocumentFragment();
1026                     let description = pauseData.insertion ? WI.UIString("Child added to ") : WI.UIString("Removed descendant ");
1027                     fragment.append(description, WI.linkifyNodeReference(node));
1028
1029                     let targetDescriptionRow = new WI.DetailsSectionSimpleRow(WI.UIString("Details"), fragment);
1030                     targetDescriptionRow.element.classList.add("target-description");
1031
1032                     this._pauseReasonGroup.rows = this._pauseReasonGroup.rows.concat(targetDescriptionRow);
1033                 });
1034
1035                 return true;
1036             }
1037             break;
1038
1039         case WI.DebuggerManager.PauseReason.Exception:
1040             console.assert(pauseData, "Expected data with an exception, but found none.");
1041             if (pauseData) {
1042                 // FIXME: We should improve the appearance of thrown objects. This works well for exception strings.
1043                 var data = WI.RemoteObject.fromPayload(pauseData, target);
1044                 this._pauseReasonTextRow.text = WI.UIString("Exception with thrown value: %s").format(data.description);
1045                 this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1046                 return true;
1047             }
1048             break;
1049
1050         case WI.DebuggerManager.PauseReason.PauseOnNextStatement:
1051             this._pauseReasonTextRow.text = WI.UIString("Immediate Pause Requested");
1052             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1053             return true;
1054
1055         case WI.DebuggerManager.PauseReason.XHR:
1056             console.assert(WI.domDebuggerManager.supported);
1057             console.assert(pauseData, "Expected XHR breakpoint data, but found none.");
1058             if (pauseData) {
1059                 if (pauseData.breakpointURL) {
1060                     let xhrBreakpoint = WI.domDebuggerManager.xhrBreakpointForURL(pauseData.breakpointURL);
1061                     console.assert(xhrBreakpoint, "Expected XHR breakpoint for URL.", pauseData.breakpointURL);
1062
1063                     this._pauseReasonTreeOutline = this.createContentTreeOutline(true);
1064
1065                     let xhrBreakpointTreeElement = new WI.XHRBreakpointTreeElement(xhrBreakpoint, WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, WI.UIString("Triggered XHR Breakpoint"));
1066                     let xhrBreakpointRow = new WI.DetailsSectionRow;
1067                     this._pauseReasonTreeOutline.appendChild(xhrBreakpointTreeElement);
1068                     xhrBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
1069
1070                     this._pauseReasonTextRow.text = pauseData.url;
1071                     this._pauseReasonGroup.rows = [xhrBreakpointRow, this._pauseReasonTextRow];
1072                 } else {
1073                     console.assert(pauseData.breakpointURL === "", "Should be the All Requests breakpoint which has an empty URL");
1074                     this._pauseReasonTextRow.text = WI.UIString("Requesting: %s").format(pauseData.url);
1075                     this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
1076                 }
1077                 this._pauseReasonTextRow.element.title = pauseData.url;
1078                 return true;
1079             }
1080             break;
1081
1082         case WI.DebuggerManager.PauseReason.Other:
1083             console.error("Paused for unknown reason. We should always have a reason.");
1084             break;
1085         }
1086
1087         return false;
1088     }
1089
1090     _updatePauseReasonGotoArrow()
1091     {
1092         this._pauseReasonLinkContainerElement.removeChildren();
1093
1094         var activeCallFrame = WI.debuggerManager.activeCallFrame;
1095         if (!activeCallFrame)
1096             return;
1097
1098         var sourceCodeLocation = activeCallFrame.sourceCodeLocation;
1099         if (!sourceCodeLocation)
1100             return;
1101
1102         const options = {
1103             useGoToArrowButton: true,
1104             ignoreNetworkTab: true,
1105             ignoreSearchTab: true,
1106         };
1107         let linkElement = WI.createSourceCodeLocationLink(sourceCodeLocation, options);
1108         this._pauseReasonLinkContainerElement.appendChild(linkElement);
1109     }
1110
1111     _addDebuggerObject(debuggerObject)
1112     {
1113         if (debuggerObject instanceof WI.Breakpoint)
1114             return this._addBreakpoint(debuggerObject);
1115
1116         if (debuggerObject instanceof WI.IssueMessage)
1117             return this._addIssue(debuggerObject);
1118
1119         return null;
1120     }
1121
1122     _addIssue(issueMessage)
1123     {
1124         let issueTreeElement = this._scriptsContentTreeOutline.findTreeElement(issueMessage);
1125         if (issueTreeElement)
1126             return issueTreeElement;
1127
1128         let parentTreeElement = this._addTreeElementForSourceCodeToTreeOutline(issueMessage.sourceCodeLocation.sourceCode, this._scriptsContentTreeOutline);
1129         if (!parentTreeElement)
1130             return null;
1131
1132         issueTreeElement = new WI.IssueTreeElement(issueMessage);
1133
1134         parentTreeElement.insertChild(issueTreeElement, insertionIndexForObjectInListSortedByFunction(issueTreeElement, parentTreeElement.children, this._compareTreeElements));
1135         if (parentTreeElement.children.length === 1)
1136             parentTreeElement.expand();
1137
1138         return issueTreeElement;
1139     }
1140
1141     _handleIssueAdded(event)
1142     {
1143         var issue = event.data.issue;
1144
1145         // We only want to show issues originating from JavaScript source code.
1146         if (!issue.sourceCodeLocation || !issue.sourceCodeLocation.sourceCode || (issue.source !== "javascript" && issue.source !== "console-api"))
1147             return;
1148
1149         this._addIssue(issue);
1150     }
1151
1152     _handleIssuesCleared(event)
1153     {
1154         let currentTreeElement = this._scriptsContentTreeOutline.children[0];
1155         let issueTreeElements = [];
1156
1157         while (currentTreeElement && !currentTreeElement.root) {
1158             if (currentTreeElement instanceof WI.IssueTreeElement)
1159                 issueTreeElements.push(currentTreeElement);
1160             currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true);
1161         }
1162
1163         issueTreeElements.forEach((treeElement) => treeElement.parent.removeChild(treeElement));
1164     }
1165
1166     _domBreakpointAddedOrRemoved(event)
1167     {
1168         if (!this._domBreakpointsContentTreeOutline.children.length) {
1169             this._domBreakpointsRow.showEmptyMessage();
1170             return;
1171         }
1172
1173         if (this._domBreakpointsContentTreeOutline.element.parent)
1174             return;
1175
1176         this._domBreakpointsRow.hideEmptyMessage();
1177         this._domBreakpointsRow.element.append(this._domBreakpointsContentTreeOutline.element);
1178
1179         this._domBreakpointsSection.collapsed = false;
1180     }
1181
1182     _addXHRBreakpointButtonClicked(event)
1183     {
1184         let popover = new WI.XHRBreakpointPopover(this);
1185         popover.show(event.target.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]);
1186     }
1187
1188     // Popover delegate
1189
1190     willDismissPopover(popover)
1191     {
1192         if (popover.result !== WI.InputPopover.Result.Committed)
1193             return;
1194
1195         let url = popover.value;
1196         if (!url)
1197             return;
1198
1199         WI.domDebuggerManager.addXHRBreakpoint(new WI.XHRBreakpoint(popover.type, url));
1200     }
1201 };
1202
1203 WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName = "paused";
1204 WI.DebuggerSidebarPanel.ExceptionIconStyleClassName = "breakpoint-exception-icon";
1205 WI.DebuggerSidebarPanel.AssertionIconStyleClassName = "breakpoint-assertion-icon";
1206 WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName = "breakpoint-paused-icon";
1207
1208 WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey = "debugger-sidebar-panel-all-exceptions-breakpoint";
1209 WI.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey = "debugger-sidebar-panel-all-uncaught-exceptions-breakpoint";
1210 WI.DebuggerSidebarPanel.SelectedAssertionsCookieKey = "debugger-sidebar-panel-assertions-breakpoint";
1211 WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey = "debugger-sidebar-panel-all-requests-breakpoint";