/* * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSidebarPanel { constructor() { super("debugger", WI.UIString("Debugger"), true); WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this); WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceAdded, this); WI.Target.addEventListener(WI.Target.Event.ResourceAdded, this._resourceAdded, this); WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this); WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.DOMBreakpointAdded, this._breakpointAdded, this); WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.EventBreakpointAdded, this._breakpointAdded, this); WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.URLBreakpointAdded, this._breakpointAdded, this); WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this); WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, this._breakpointRemoved, this); WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.EventBreakpointRemoved, this._breakpointRemoved, this); WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.URLBreakpointRemoved, this._breakpointRemoved, this); WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this); WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this); WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptRemoved, this._scriptRemoved, this); WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptsCleared, this._scriptsCleared, this); WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, this._debuggerDidPause, this); WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Resumed, this._debuggerDidResume, this); WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.CallFramesDidChange, this._debuggerCallFramesDidChange, this); WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this._debuggerActiveCallFrameDidChange, this); WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.WaitingToPause, this._debuggerWaitingToPause, this); WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeChanged, this._handleDOMBreakpointDOMNodeChanged, this); WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this); WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditManagerTestScheduled, this); WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditManagerTestCompleted, this); WI.targetManager.addEventListener(WI.TargetManager.Event.TargetAdded, this._targetAdded, this); WI.targetManager.addEventListener(WI.TargetManager.Event.TargetRemoved, this._targetRemoved, this); this._timelineRecordingWarningElement = document.createElement("div"); this._timelineRecordingWarningElement.classList.add("warning-banner"); this._timelineRecordingWarningElement.append(WI.UIString("Debugger disabled during Timeline recording"), document.createElement("br")); let timelineStopRecordingLink = this._timelineRecordingWarningElement.appendChild(document.createElement("a")); timelineStopRecordingLink.textContent = WI.UIString("Stop recording"); timelineStopRecordingLink.addEventListener("click", () => { WI.timelineManager.stopCapturing(); }); this._auditTestWarningElement = document.createElement("div"); this._auditTestWarningElement.classList.add("warning-banner"); this._auditTestWarningElement.append(WI.UIString("Debugger disabled during Audit"), document.createElement("br")); let auditStopRecordingLink = this._auditTestWarningElement.appendChild(document.createElement("a")); auditStopRecordingLink.textContent = WI.UIString("Stop Audit"); auditStopRecordingLink.addEventListener("click", () => { WI.auditManager.stop(); }); this._breakpointsDisabledWarningElement = document.createElement("div"); this._breakpointsDisabledWarningElement.classList.add("warning-banner"); this._breakpointsDisabledWarningElement.append(WI.UIString("Breakpoints disabled"), document.createElement("br")); let enableBreakpointsLink = this._breakpointsDisabledWarningElement.appendChild(document.createElement("a")); enableBreakpointsLink.textContent = WI.UIString("Enable breakpoints"); enableBreakpointsLink.addEventListener("click", () => { WI.debuggerToggleBreakpoints(); }); this._navigationBar = new WI.NavigationBar; this.addSubview(this._navigationBar); var breakpointsImage = {src: "Images/Breakpoints.svg", width: 15, height: 15}; var pauseImage = {src: "Images/Pause.svg", width: 15, height: 15}; var resumeImage = {src: "Images/Resume.svg", width: 15, height: 15}; var stepOverImage = {src: "Images/StepOver.svg", width: 15, height: 15}; var stepIntoImage = {src: "Images/StepInto.svg", width: 15, height: 15}; var stepOutImage = {src: "Images/StepOut.svg", width: 15, height: 15}; var toolTip = WI.UIString("Enable all breakpoints (%s)").format(WI.toggleBreakpointsKeyboardShortcut.displayName); var altToolTip = WI.UIString("Disable all breakpoints (%s)").format(WI.toggleBreakpointsKeyboardShortcut.displayName); this._debuggerBreakpointsButtonItem = new WI.ActivateButtonNavigationItem("debugger-breakpoints", toolTip, altToolTip, breakpointsImage.src, breakpointsImage.width, breakpointsImage.height); this._debuggerBreakpointsButtonItem.activated = WI.debuggerManager.breakpointsEnabled; this._debuggerBreakpointsButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerToggleBreakpoints, this); this._navigationBar.addNavigationItem(this._debuggerBreakpointsButtonItem); toolTip = WI.UIString("Pause script execution (%s or %s)").format(WI.pauseOrResumeKeyboardShortcut.displayName, WI.pauseOrResumeAlternateKeyboardShortcut.displayName); altToolTip = WI.UIString("Continue script execution (%s or %s)").format(WI.pauseOrResumeKeyboardShortcut.displayName, WI.pauseOrResumeAlternateKeyboardShortcut.displayName); this._debuggerPauseResumeButtonItem = new WI.ToggleButtonNavigationItem("debugger-pause-resume", toolTip, altToolTip, pauseImage.src, resumeImage.src, pauseImage.width, pauseImage.height); this._debuggerPauseResumeButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerPauseResumeToggle, this); this._navigationBar.addNavigationItem(this._debuggerPauseResumeButtonItem); 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); this._debuggerStepOverButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepOver, this); this._debuggerStepOverButtonItem.enabled = false; this._navigationBar.addNavigationItem(this._debuggerStepOverButtonItem); 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); this._debuggerStepIntoButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepInto, this); this._debuggerStepIntoButtonItem.enabled = false; this._navigationBar.addNavigationItem(this._debuggerStepIntoButtonItem); 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); this._debuggerStepOutButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepOut, this); this._debuggerStepOutButtonItem.enabled = false; this._navigationBar.addNavigationItem(this._debuggerStepOutButtonItem); function showResourcesWithIssuesOnlyFilterFunction(treeElement) { // Issues are only shown in the scripts tree outline. if (treeElement.treeOutline !== this._scriptsContentTreeOutline) return true; // Keep issues. if (treeElement instanceof WI.IssueTreeElement) return true; // Keep resources with issues. if (treeElement.hasChildren) { for (let child of treeElement.children) { if (child instanceof WI.IssueTreeElement) return true; } } return false; } 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); this._breakpointsContentTreeOutline = this.contentTreeOutline; let breakpointsRow = new WI.DetailsSectionRow; breakpointsRow.element.appendChild(this._breakpointsContentTreeOutline.element); let breakpointNavigationBarWrapper = document.createElement("div"); let breakpointNavigationBar = new WI.NavigationBar; breakpointNavigationBarWrapper.appendChild(breakpointNavigationBar.element); this._createBreakpointButton = new WI.ButtonNavigationItem("create-breakpoint", WI.UIString("Create Breakpoint"), "Images/Plus13.svg", 13, 13); this._createBreakpointButton.element.addEventListener("mousedown", this._handleCreateBreakpointMouseDown.bind(this)); breakpointNavigationBar.addNavigationItem(this._createBreakpointButton); let breakpointsGroup = new WI.DetailsSectionGroup([breakpointsRow]); this._breakpointsSection = new WI.DetailsSection("breakpoints", WI.UIString("Breakpoints"), [breakpointsGroup], breakpointNavigationBarWrapper); this.contentView.element.appendChild(this._breakpointsSection.element); this._breakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.ElementAdded, this._handleBreakpointElementAddedOrRemoved, this); this._breakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.ElementRemoved, this._handleBreakpointElementAddedOrRemoved, this); this._breakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this); this._breakpointsContentTreeOutline.ondelete = this._breakpointTreeOutlineDeleteTreeElement.bind(this); this._breakpointsContentTreeOutline.populateContextMenu = function(contextMenu, event, treeElement) { this._breakpointTreeOutlineContextMenuTreeElement(contextMenu, event, treeElement); WI.TreeOutline.prototype.populateContextMenu(contextMenu, event, treeElement); }.bind(this); this._scriptsContentTreeOutline = this.createContentTreeOutline(); this._scriptsContentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this); this._scriptsContentTreeOutline.includeSourceMapResourceChildren = true; let scriptsRow = new WI.DetailsSectionRow; scriptsRow.element.appendChild(this._scriptsContentTreeOutline.element); let scriptsGroup = new WI.DetailsSectionGroup([scriptsRow]); this._scriptsSection = new WI.DetailsSection("scripts", WI.UIString("Sources"), [scriptsGroup]); this.contentView.element.appendChild(this._scriptsSection.element); this._callStackTreeOutline = this.createContentTreeOutline({suppressFiltering: true}); this._callStackTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this); this._callStackRow = new WI.DetailsSectionRow; this._callStackRow.element.appendChild(this._callStackTreeOutline.element); this._callStackGroup = new WI.DetailsSectionGroup([this._callStackRow]); this._callStackSection = new WI.DetailsSection("call-stack", WI.UIString("Call Stack"), [this._callStackGroup]); this._mainTargetTreeElement = null; this._activeCallFrameTreeElement = null; this._pauseReasonTreeOutline = null; this._pauseReasonLinkContainerElement = document.createElement("span"); this._pauseReasonTextRow = new WI.DetailsSectionTextRow; this._pauseReasonGroup = new WI.DetailsSectionGroup([this._pauseReasonTextRow]); this._pauseReasonSection = new WI.DetailsSection("paused-reason", WI.UIString("Pause Reason"), [this._pauseReasonGroup], this._pauseReasonLinkContainerElement); WI.Breakpoint.addEventListener(WI.Breakpoint.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this); WI.IssueMessage.addEventListener(WI.IssueMessage.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this); WI.consoleManager.addEventListener(WI.ConsoleManager.Event.IssueAdded, this._handleIssueAdded, this); WI.consoleManager.addEventListener(WI.ConsoleManager.Event.Cleared, this._handleIssuesCleared, this); WI.debuggerManager.addBreakpoint(WI.debuggerManager.allExceptionsBreakpoint); WI.debuggerManager.addBreakpoint(WI.debuggerManager.uncaughtExceptionsBreakpoint); // COMPATIBILITY (iOS 10): DebuggerAgent.setPauseOnAssertions did not exist yet. if (InspectorBackend.domains.Debugger.setPauseOnAssertions && WI.settings.showAssertionFailuresBreakpoint.value) WI.debuggerManager.addBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint); for (let target of WI.targets) this._addTarget(target); this._updateCallStackTreeOutline(); if (WI.networkManager.mainFrame) this._addResourcesRecursivelyForFrame(WI.networkManager.mainFrame); for (let script of WI.debuggerManager.knownNonResourceScripts) this._addScript(script); if (WI.domDebuggerManager.supported) { if (WI.settings.showAllRequestsBreakpoint.value) WI.domDebuggerManager.addURLBreakpoint(WI.domDebuggerManager.allRequestsBreakpoint); for (let eventBreakpoint of WI.domDebuggerManager.eventBreakpoints) this._addBreakpoint(eventBreakpoint); for (let domBreakpoint of WI.domDebuggerManager.domBreakpoints) this._addBreakpoint(domBreakpoint); for (let eventListenerBreakpoint of WI.domManager.eventListenerBreakpoints) this._addBreakpoint(eventListenerBreakpoint); for (let urlBreakpoints of WI.domDebuggerManager.urlBreakpoints) this._addBreakpoint(urlBreakpoints); } if (WI.debuggerManager.paused) this._debuggerDidPause(null); if (WI.debuggerManager.breakpointsDisabledTemporarily) { this._handleTimelineCapturingStateChanged(); if (WI.auditManager.runningState === WI.AuditManager.RunningState.Active || WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping) this._handleAuditManagerTestScheduled(); } this._updateBreakpointsDisabledBanner(); } // Public get minimumWidth() { return this._navigationBar.minimumWidth; } closed() { super.closed(); WI.Frame.removeEventListener(null, null, this); WI.Target.removeEventListener(null, null, this); WI.debuggerManager.removeEventListener(null, null, this); WI.domDebuggerManager.removeEventListener(null, null, this); WI.DOMBreakpoint.removeEventListener(null, null, this); WI.timelineManager.removeEventListener(null, null, this); WI.targetManager.removeEventListener(null, null, this); WI.Breakpoint.removeEventListener(null, null, this); WI.IssueMessage.removeEventListener(null, null, this); } showDefaultContentView() { if (WI.networkManager.mainFrame) { let mainTreeElement = this._scriptsContentTreeOutline.findTreeElement(WI.networkManager.mainFrame.mainResource); if (mainTreeElement && this.showDefaultContentViewForTreeElement(mainTreeElement)) return; } let currentTreeElement = this._scriptsContentTreeOutline.children[0]; while (currentTreeElement && !currentTreeElement.root) { if (currentTreeElement instanceof WI.ResourceTreeElement || currentTreeElement instanceof WI.ScriptTreeElement) { if (this.showDefaultContentViewForTreeElement(currentTreeElement)) return; } currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true); } } treeElementForRepresentedObject(representedObject) { // The main resource is used as the representedObject instead of Frame in our tree. if (representedObject instanceof WI.Frame) representedObject = representedObject.mainResource; let treeElement = this._breakpointsContentTreeOutline.findTreeElement(representedObject); if (!treeElement) treeElement = this._scriptsContentTreeOutline.findTreeElement(representedObject); if (treeElement) return treeElement; // Only special case Script objects. if (!(representedObject instanceof WI.Script)) { console.error("Didn't find a TreeElement for representedObject", representedObject); return null; } // If the Script has a URL we should have found it earlier. if (representedObject.url) { console.error("Didn't find a ScriptTreeElement for a Script with a URL."); return null; } // Since the Script does not have a URL we consider it an 'anonymous' script. These scripts happen from calls to // window.eval() or browser features like Auto Fill and Reader. They are not normally added to the sidebar, but since // we have a ScriptContentView asking for the tree element we will make a ScriptTreeElement on demand and add it. return this._addScript(representedObject); } createContentTreeOutline(options = {}) { let treeOutline = super.createContentTreeOutline(options) treeOutline.addEventListener(WI.TreeOutline.Event.ElementRevealed, (event) => { let treeElement = event.data.element; let detailsSections = [this._pauseReasonSection, this._callStackSection, this._breakpointsSection, this._scriptsSection]; let detailsSection = detailsSections.find((detailsSection) => detailsSection.element.contains(treeElement.listItemElement)); if (!detailsSection) return; // Revealing a TreeElement at the scroll container's topmost edge with // scrollIntoViewIfNeeded may result in the element being covered by the // DetailsSection header, which uses sticky positioning. Detect this case, // and adjust the sidebar content's scroll position to compensate. let offset = detailsSection.headerElement.totalOffsetBottom - treeElement.listItemElement.totalOffsetTop; if (offset > 0) this.scrollElement.scrollBy(0, -offset); }); return treeOutline; } // Protected saveStateToCookie(cookie) { console.assert(cookie); var selectedTreeElement = this._breakpointsContentTreeOutline.selectedTreeElement; if (!selectedTreeElement) { super.saveStateToCookie(cookie); return; } var representedObject = selectedTreeElement.representedObject; if (representedObject === WI.debuggerManager.allExceptionsBreakpoint) { cookie[WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey] = true; return; } if (representedObject === WI.debuggerManager.uncaughtExceptionsBreakpoint) { cookie[WI.DebuggerSidebarPanel.SelectedUncaughtExceptionsCookieKey] = true; return; } if (representedObject === WI.debuggerManager.assertionFailuresBreakpoint) { cookie[WI.DebuggerSidebarPanel.SelectedAssertionFailuresCookieKey] = true; return; } if (representedObject === WI.domDebuggerManager.allRequestsBreakpoint) { cookie[WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey] = true; return; } super.saveStateToCookie(cookie); } restoreStateFromCookie(cookie, relaxedMatchDelay) { console.assert(cookie); function revealAndSelect(treeOutline, breakpoint) { let treeElement = treeOutline.findTreeElement(breakpoint); if (!treeElement) return; treeElement.revealAndSelect(); } // Eagerly resolve the special breakpoints; otherwise, use the default behavior. if (cookie[WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey]) revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.allExceptionsBreakpoint); else if (cookie[WI.DebuggerSidebarPanel.SelectedUncaughtExceptionsCookieKey]) revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.uncaughtExceptionsBreakpoint); else if (cookie[WI.DebuggerSidebarPanel.SelectedAssertionFailuresCookieKey]) revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.assertionFailuresBreakpoint); else if (cookie[WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey]) revealAndSelect(this._breakpointsContentTreeOutline, WI.domDebuggerManager.allRequestsBreakpoint); else super.restoreStateFromCookie(cookie, relaxedMatchDelay); } // Popover delegate willDismissPopover(popover) { let breakpoint = popover.breakpoint; if (!breakpoint) return; if (breakpoint instanceof WI.EventBreakpoint) WI.domDebuggerManager.addEventBreakpoint(breakpoint); else if (breakpoint instanceof WI.URLBreakpoint) WI.domDebuggerManager.addURLBreakpoint(breakpoint); } // Private _debuggerWaitingToPause(event) { this._debuggerPauseResumeButtonItem.enabled = false; } _debuggerDidPause(event) { this.contentView.element.insertBefore(this._callStackSection.element, this.contentView.element.firstChild); if (this._updatePauseReason()) this.contentView.element.insertBefore(this._pauseReasonSection.element, this.contentView.element.firstChild); this._debuggerPauseResumeButtonItem.enabled = true; this._debuggerPauseResumeButtonItem.toggled = true; this._debuggerStepOverButtonItem.enabled = true; this._debuggerStepIntoButtonItem.enabled = true; this._debuggerStepOutButtonItem.enabled = true; this.element.classList.add(WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName); } _debuggerDidResume(event) { this._callStackSection.element.remove(); this._pauseReasonSection.element.remove(); this._debuggerPauseResumeButtonItem.enabled = true; this._debuggerPauseResumeButtonItem.toggled = false; this._debuggerStepOverButtonItem.enabled = false; this._debuggerStepIntoButtonItem.enabled = false; this._debuggerStepOutButtonItem.enabled = false; this.element.classList.remove(WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName); } _breakpointsEnabledDidChange(event) { this._debuggerBreakpointsButtonItem.activated = WI.debuggerManager.breakpointsEnabled; this._updateBreakpointsDisabledBanner(); } _addBreakpoint(breakpoint) { let constructor = WI.BreakpointTreeElement; let options = {}; let parentTreeElement = this._breakpointsContentTreeOutline; let getDOMNodeTreeElement = (domNode) => { console.assert(domNode, "Missing DOMNode for identifier", breakpoint.domNodeIdentifier); if (!domNode) return null; let domNodeTreeElement = this._breakpointsContentTreeOutline.findTreeElement(domNode); if (!domNodeTreeElement) { domNodeTreeElement = new WI.DOMNodeTreeElement(domNode); this._addTreeElement(domNodeTreeElement, parentTreeElement); } return domNodeTreeElement; }; if (breakpoint === WI.debuggerManager.allExceptionsBreakpoint) { options.className = WI.DebuggerSidebarPanel.ExceptionIconStyleClassName; options.title = WI.repeatedUIString.allExceptions(); } else if (breakpoint === WI.debuggerManager.uncaughtExceptionsBreakpoint) { options.className = WI.DebuggerSidebarPanel.ExceptionIconStyleClassName; options.title = WI.repeatedUIString.uncaughtExceptions(); } else if (breakpoint === WI.debuggerManager.assertionFailuresBreakpoint) { options.className = WI.DebuggerSidebarPanel.AssertionIconStyleClassName; options.title = WI.repeatedUIString.assertionFailures(); } else if (breakpoint instanceof WI.DOMBreakpoint) { if (!breakpoint.domNodeIdentifier) return null; constructor = WI.DOMBreakpointTreeElement; let domNode = WI.domManager.nodeForId(breakpoint.domNodeIdentifier); parentTreeElement = getDOMNodeTreeElement(domNode); } else if (breakpoint instanceof WI.EventBreakpoint) { constructor = WI.EventBreakpointTreeElement; if (breakpoint.eventListener) { let eventTargetTreeElement = null; if (breakpoint.eventListener.onWindow) { if (!DebuggerSidebarPanel.__windowEventTargetRepresentedObject) DebuggerSidebarPanel.__windowEventTargetRepresentedObject = {__window: true}; eventTargetTreeElement = this._breakpointsContentTreeOutline.findTreeElement(DebuggerSidebarPanel.__windowEventTargetRepresentedObject); if (!eventTargetTreeElement) { const subtitle = null; eventTargetTreeElement = new WI.GeneralTreeElement(["event-target-window"], WI.unlocalizedString("window"), subtitle, DebuggerSidebarPanel.__windowEventTargetRepresentedObject); this._addTreeElement(eventTargetTreeElement, parentTreeElement); } } else if (breakpoint.eventListener.node) eventTargetTreeElement = getDOMNodeTreeElement(breakpoint.eventListener.node); if (eventTargetTreeElement) parentTreeElement = eventTargetTreeElement; } } else if (breakpoint instanceof WI.URLBreakpoint) { constructor = WI.URLBreakpointTreeElement; if (breakpoint === WI.domDebuggerManager.allRequestsBreakpoint) options.title = WI.repeatedUIString.allRequests(); } else { let sourceCode = breakpoint.sourceCodeLocation && breakpoint.sourceCodeLocation.displaySourceCode; if (!sourceCode) return null; if (this._breakpointsContentTreeOutline.findTreeElement(breakpoint)) return null; parentTreeElement = this._addTreeElementForSourceCodeToTreeOutline(sourceCode, this._breakpointsContentTreeOutline); // Mark disabled breakpoints as resolved if there is source code loaded with that URL. // This gives the illusion the breakpoint was resolved, but since we don't send disabled // breakpoints to the backend we don't know for sure. If the user enables the breakpoint // it will be resolved properly. if (breakpoint.disabled) breakpoint.resolved = true; } let breakpointTreeElement = new constructor(breakpoint, options); this._addTreeElement(breakpointTreeElement, parentTreeElement); if (parentTreeElement.children.length === 1) parentTreeElement.expand(); return breakpointTreeElement; } _removeBreakpoint(breakpoint) { if (this._pauseReasonTreeOutline) { let pauseReasonBreakpointTreeElement = this._pauseReasonTreeOutline.getCachedTreeElement(breakpoint); if (pauseReasonBreakpointTreeElement) pauseReasonBreakpointTreeElement.status = null; } let breakpointTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(breakpoint); if (!breakpointTreeElement) return; this._removeDebuggerTreeElement(breakpointTreeElement); } _addBreakpointsForSourceCode(sourceCode) { for (let breakpoint of WI.debuggerManager.breakpointsForSourceCode(sourceCode)) this._addBreakpoint(breakpoint, sourceCode); } _addIssuesForSourceCode(sourceCode) { var issues = WI.consoleManager.issuesForSourceCode(sourceCode); for (var issue of issues) this._addIssue(issue); } _addTreeElementForSourceCodeToTreeOutline(sourceCode, treeOutline) { let treeElement = treeOutline.getCachedTreeElement(sourceCode); if (!treeElement) { if (sourceCode instanceof WI.SourceMapResource) treeElement = new WI.SourceMapResourceTreeElement(sourceCode); else if (sourceCode instanceof WI.Resource) treeElement = new WI.ResourceTreeElement(sourceCode); else if (sourceCode instanceof WI.Script) treeElement = new WI.ScriptTreeElement(sourceCode); } if (!treeElement) { console.error("Unknown sourceCode instance", sourceCode); return null; } if (!treeElement.parent) { treeElement.hasChildren = false; treeElement.expand(); this._addTreeElement(treeElement, treeOutline); } return treeElement; } _addResourcesRecursivelyForFrame(frame) { this._addResource(frame.mainResource); for (let resource of frame.resourceCollection) this._addResource(resource); for (let childFrame of frame.childFrameCollection) this._addResourcesRecursivelyForFrame(childFrame); } _resourceAdded(event) { this._addResource(event.data.resource); } _addResource(resource) { if (![WI.Resource.Type.Document, WI.Resource.Type.Script].includes(resource.type)) return; let treeElement = this._addTreeElementForSourceCodeToTreeOutline(resource, this._scriptsContentTreeOutline); this._addBreakpointsForSourceCode(resource); this._addIssuesForSourceCode(resource); if (this.parentSidebar && !this.contentBrowser.currentContentView) this.showDefaultContentViewForTreeElement(treeElement); } _mainResourceDidChange(event) { if (event.target.isMainFrame()) { // Aggressively prune resources now so the old resources are removed before // the new main resource is added below. This avoids a visual flash when the // prune normally happens on a later event loop cycle. this.pruneStaleResourceTreeElements(); this.contentBrowser.contentViewContainer.closeAllContentViews(); for (let domBreakpoint of WI.domDebuggerManager.domBreakpoints) this._removeBreakpoint(domBreakpoint); } if (!event.data.oldMainResource) { var resource = event.target.mainResource; this._addTreeElementForSourceCodeToTreeOutline(resource, this._scriptsContentTreeOutline); this._addBreakpointsForSourceCode(resource); this._addIssuesForSourceCode(resource); } } _handleTimelineCapturingStateChanged(event) { this._updateTemporarilyDisabledBreakpointsButtons(); switch (WI.timelineManager.capturingState) { case WI.TimelineManager.CapturingState.Starting: this.contentView.element.insertBefore(this._timelineRecordingWarningElement, this.contentView.element.firstChild); break; case WI.TimelineManager.CapturingState.Inactive: this._timelineRecordingWarningElement.remove(); break; } this._updateBreakpointsDisabledBanner(); } _handleAuditManagerTestScheduled(event) { this._updateTemporarilyDisabledBreakpointsButtons(); this.contentView.element.insertBefore(this._auditTestWarningElement, this.contentView.element.firstChild); this._updateBreakpointsDisabledBanner(); } _handleAuditManagerTestCompleted(event) { this._updateTemporarilyDisabledBreakpointsButtons(); this._auditTestWarningElement.remove(); this._updateBreakpointsDisabledBanner(); } _updateTemporarilyDisabledBreakpointsButtons() { let breakpointsDisabledTemporarily = WI.debuggerManager.breakpointsDisabledTemporarily; this._debuggerBreakpointsButtonItem.enabled = !breakpointsDisabledTemporarily; this._debuggerPauseResumeButtonItem.enabled = !breakpointsDisabledTemporarily; } _updateBreakpointsDisabledBanner() { let breakpointsEnabled = WI.debuggerManager.breakpointsEnabled; let timelineWarningShowing = !!this._timelineRecordingWarningElement.parentElement; let auditWarningShowing = !!this._auditTestWarningElement.parentElement; if (!breakpointsEnabled && !timelineWarningShowing && !auditWarningShowing) this.contentView.element.insertBefore(this._breakpointsDisabledWarningElement, this.contentView.element.firstChild); else this._breakpointsDisabledWarningElement.remove(); } _scriptAdded(event) { this._addScript(event.data.script); } _addScript(script) { // COMPATIBILITY(iOS 9): Backends could send the frontend built-in code, filter out JSC internals. if (!script.url && !script.sourceURL) return null; // In general, do not show dynamically added script elements. if (script.dynamicallyAddedScriptElement && !script.sourceURL) return null; // Don't add breakpoints if the script is represented by a Resource. They were // already added by _resourceAdded. if (script.resource) return null; let treeElement = this._addTreeElementForSourceCodeToTreeOutline(script, this._scriptsContentTreeOutline); this._addBreakpointsForSourceCode(script); this._addIssuesForSourceCode(script); if (this.parentSidebar && !this.contentBrowser.currentContentView) this.showDefaultContentViewForTreeElement(treeElement); return treeElement; } _scriptRemoved(event) { function removeScript(script, treeOutline) { let scriptTreeElement = treeOutline.getCachedTreeElement(script); if (scriptTreeElement) scriptTreeElement.parent.removeChild(scriptTreeElement); } let script = event.data.script; removeScript(script, this._breakpointsContentTreeOutline); removeScript(script, this._scriptsContentTreeOutline); } _scriptsCleared(event) { for (var i = this._breakpointsContentTreeOutline.children.length - 1; i >= 0; --i) { var treeElement = this._breakpointsContentTreeOutline.children[i]; if (!(treeElement instanceof WI.ScriptTreeElement)) continue; this._breakpointsContentTreeOutline.removeChildAtIndex(i, true, true); } this._scriptsContentTreeOutline.removeChildren(); this._addResourcesRecursivelyForFrame(WI.networkManager.mainFrame); } _breakpointAdded(event) { var breakpoint = event.data.breakpoint; this._addBreakpoint(breakpoint); } _breakpointRemoved(event) { var breakpoint = event.data.breakpoint; this._removeBreakpoint(breakpoint); } _findThreadTreeElementForTarget(target) { for (let child of this._callStackTreeOutline.children) { if (child.target === target) return child; } return null; } _targetAdded(event) { this._addTarget(event.data.target); } _addTarget(target) { let treeElement = new WI.ThreadTreeElement(target); this._callStackTreeOutline.appendChild(treeElement); // FIXME: When WI.mainTarget changes? if (target === WI.mainTarget) this._mainTargetTreeElement = treeElement; this._updateCallStackTreeOutline(); } _targetRemoved(event) { let target = event.data.target; let treeElement = this._findThreadTreeElementForTarget(target); this._callStackTreeOutline.removeChild(treeElement); this._updateCallStackTreeOutline(); } _updateCallStackTreeOutline() { let singleThreadShowing = WI.targets.length <= 1; this._callStackTreeOutline.element.classList.toggle("single-thread", singleThreadShowing); if (this._mainTargetTreeElement) this._mainTargetTreeElement.selectable = !singleThreadShowing; } _handleDebuggerObjectDisplayLocationDidChange(event) { var debuggerObject = event.target; if (event.data.oldDisplaySourceCode === debuggerObject.sourceCodeLocation.displaySourceCode) return; // A known debugger object (breakpoint, issueMessage, etc.) moved between resources. Remove // the old tree element and create a new tree element with the updated file. let wasSelected = false; let oldDebuggerTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(debuggerObject); if (oldDebuggerTreeElement) wasSelected = oldDebuggerTreeElement.selected; let newDebuggerTreeElement = null; if (debuggerObject instanceof WI.Breakpoint) newDebuggerTreeElement = this._addBreakpoint(debuggerObject); else if (debuggerObject instanceof WI.IssueMessage) newDebuggerTreeElement = this._addIssue(debuggerObject); if (!newDebuggerTreeElement) return; if (oldDebuggerTreeElement) this._removeDebuggerTreeElement(oldDebuggerTreeElement); if (wasSelected) newDebuggerTreeElement.revealAndSelect(true, false, true); } _removeDebuggerTreeElement(debuggerTreeElement) { // If this is a BreakpointTreeElement being deleted because of a cause // outside of the TreeOutline then deselect if it is selected to avoid // TreeOutline selection changes causing unexpected ContentView changes. if (!debuggerTreeElement.__deletedViaDeleteKeyboardShortcut) debuggerTreeElement.deselect(); let parentTreeElement = debuggerTreeElement.parent; parentTreeElement.removeChild(debuggerTreeElement); if (parentTreeElement.children.length || parentTreeElement === this._breakpointsContentTreeOutline) return; parentTreeElement.treeOutline.removeChild(parentTreeElement); } _debuggerCallFramesDidChange(event) { let target = event.data.target; let treeElement = this._findThreadTreeElementForTarget(target); treeElement.refresh(); let activeCallFrameTreeElement = this._callStackTreeOutline.findTreeElement(WI.debuggerManager.activeCallFrame); if (activeCallFrameTreeElement) activeCallFrameTreeElement.reveal(); } _debuggerActiveCallFrameDidChange() { if (this._activeCallFrameTreeElement) { this._activeCallFrameTreeElement.isActiveCallFrame = false; this._activeCallFrameTreeElement = null; } if (!WI.debuggerManager.activeCallFrame) return; this._activeCallFrameTreeElement = this._callStackTreeOutline.findTreeElement(WI.debuggerManager.activeCallFrame); if (this._activeCallFrameTreeElement) this._activeCallFrameTreeElement.isActiveCallFrame = true; } _breakpointsBeneathTreeElement(treeElement) { console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement); if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement)) return []; var breakpoints = []; var breakpointTreeElements = treeElement.children; for (var i = 0; i < breakpointTreeElements.length; ++i) { console.assert(breakpointTreeElements[i] instanceof WI.BreakpointTreeElement); console.assert(breakpointTreeElements[i].breakpoint); var breakpoint = breakpointTreeElements[i].breakpoint; if (breakpoint) breakpoints.push(breakpoint); } return breakpoints; } _removeAllBreakpoints(breakpoints) { for (var i = 0; i < breakpoints.length; ++i) { var breakpoint = breakpoints[i]; if (WI.debuggerManager.isBreakpointRemovable(breakpoint)) WI.debuggerManager.removeBreakpoint(breakpoint); } } _toggleAllBreakpoints(breakpoints, disabled) { for (var i = 0; i < breakpoints.length; ++i) breakpoints[i].disabled = disabled; } _breakpointTreeOutlineDeleteTreeElement(treeElement) { console.assert(treeElement.selected); if (treeElement.representedObject === DebuggerSidebarPanel.__windowEventTargetRepresentedObject) { let eventBreakpointsOnWindow = WI.domManager.eventListenerBreakpoints.filter((eventBreakpoint) => eventBreakpoint.eventListener.onWindow); for (let eventBreakpoint of eventBreakpointsOnWindow) WI.domManager.removeBreakpointForEventListener(eventBreakpoint.eventListener); return true; } console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement); if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement)) return false; var wasTopResourceTreeElement = treeElement.previousSibling === this._assertionsBreakpointTreeElement || treeElement.previousSibling === this._allUncaughtExceptionsBreakpointTreeElement; var nextSibling = treeElement.nextSibling; var breakpoints = this._breakpointsBeneathTreeElement(treeElement); this._removeAllBreakpoints(breakpoints); if (wasTopResourceTreeElement && nextSibling) nextSibling.select(true, true); return true; } _breakpointTreeOutlineContextMenuTreeElement(contextMenu, event, treeElement) { // This check is necessary since the context menu is created by the TreeOutline, meaning // that any child could be the target of the context menu event. if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement)) return; let breakpoints = this._breakpointsBeneathTreeElement(treeElement); let shouldDisable = breakpoints.some((breakpoint) => !breakpoint.disabled); let removeAllResourceBreakpoints = () => { this._removeAllBreakpoints(breakpoints); }; let toggleAllResourceBreakpoints = () => { this._toggleAllBreakpoints(breakpoints, shouldDisable); }; if (shouldDisable) contextMenu.appendItem(WI.UIString("Disable Breakpoints"), toggleAllResourceBreakpoints); else contextMenu.appendItem(WI.UIString("Enable Breakpoints"), toggleAllResourceBreakpoints); contextMenu.appendItem(WI.UIString("Delete Breakpoints"), removeAllResourceBreakpoints); } _treeSelectionDidChange(event) { if (!this.selected) return; let treeElement = event.target.selectedTreeElement; if (!treeElement) return; if (treeElement instanceof WI.DOMNodeTreeElement || treeElement instanceof WI.DOMBreakpointTreeElement || treeElement instanceof WI.EventBreakpointTreeElement || treeElement instanceof WI.URLBreakpointTreeElement) return; if (treeElement.representedObject === DebuggerSidebarPanel.__windowEventTargetRepresentedObject) return; const options = { ignoreNetworkTab: true, ignoreSearchTab: true, }; if (treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement) { WI.showSourceCode(treeElement.representedObject, options); return; } if (treeElement instanceof WI.CallFrameTreeElement) { let callFrame = treeElement.callFrame; if (callFrame.id) WI.debuggerManager.activeCallFrame = callFrame; if (callFrame.sourceCodeLocation) WI.showSourceCodeLocation(callFrame.sourceCodeLocation, options); return; } if (treeElement instanceof WI.IssueTreeElement) { WI.showSourceCodeLocation(treeElement.issueMessage.sourceCodeLocation, options); return; } if (!(treeElement instanceof WI.BreakpointTreeElement)) return; let breakpoint = treeElement.breakpoint; if (treeElement.treeOutline === this._pauseReasonTreeOutline) { WI.showSourceCodeLocation(breakpoint.sourceCodeLocation, options); return; } if (!treeElement.parent.representedObject) return; console.assert(treeElement.parent.representedObject instanceof WI.SourceCode); if (!(treeElement.parent.representedObject instanceof WI.SourceCode)) return; WI.showSourceCodeLocation(breakpoint.sourceCodeLocation, options); } _addTreeElement(treeElement, parentTreeElement) { if (!parentTreeElement) parentTreeElement = this._breakpointsContentTreeOutline; let comparator = (a, b) => { const rankFunctions = [ (treeElement) => treeElement.representedObject === WI.debuggerManager.allExceptionsBreakpoint, (treeElement) => treeElement.representedObject === WI.debuggerManager.uncaughtExceptionsBreakpoint, (treeElement) => treeElement.representedObject === WI.debuggerManager.assertionFailuresBreakpoint, (treeElement) => treeElement instanceof WI.BreakpointTreeElement || treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement, (treeElement) => treeElement instanceof WI.EventBreakpointTreeElement, (treeElement) => treeElement instanceof WI.DOMNodeTreeElement, (treeElement) => treeElement.representedObject === DebuggerSidebarPanel.__windowEventTargetRepresentedObject, (treeElement) => treeElement instanceof WI.DOMBreakpointTreeElement, (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allRequestsBreakpoint, (treeElement) => treeElement instanceof WI.URLBreakpointTreeElement, ]; let aRank = rankFunctions.findIndex((rankFunction) => rankFunction(a)); let bRank = rankFunctions.findIndex((rankFunction) => rankFunction(b)); if (aRank >= 0 && bRank >= 0) { if (aRank < bRank) return -1; if (bRank < aRank) return 1; } if (a instanceof WI.BreakpointTreeElement && b instanceof WI.BreakpointTreeElement) return this._compareBreakpointTreeElements(a, b); return a.mainTitle.extendedLocaleCompare(b.mainTitle) || a.subtitle.extendedLocaleCompare(b.subtitle); }; parentTreeElement.insertChild(treeElement, insertionIndexForObjectInListSortedByFunction(treeElement, parentTreeElement.children, comparator)); } _compareBreakpointTreeElements(a, b) { if (!a.representedObject || !b.representedObject) return 0; let aLocation = a.representedObject.sourceCodeLocation; let bLocation = b.representedObject.sourceCodeLocation; if (!aLocation || !bLocation) return 0; var comparisonResult = aLocation.displayLineNumber - bLocation.displayLineNumber; if (comparisonResult !== 0) return comparisonResult; return aLocation.displayColumnNumber - bLocation.displayColumnNumber; } _updatePauseReason() { this._pauseReasonTreeOutline = null; this._updatePauseReasonGotoArrow(); return this._updatePauseReasonSection(); } _updatePauseReasonSection() { let target = WI.debuggerManager.activeCallFrame.target; let targetData = WI.debuggerManager.dataForTarget(target); let {pauseReason, pauseData} = targetData; switch (pauseReason) { case WI.DebuggerManager.PauseReason.AnimationFrame: console.assert(pauseData, "Expected data with an animation frame, but found none."); if (!pauseData) return false; var eventBreakpoint = WI.domDebuggerManager.eventBreakpointForTypeAndEventName(WI.EventBreakpoint.Type.AnimationFrame, pauseData.eventName); console.assert(eventBreakpoint, "Expected AnimationFrame breakpoint for event name.", pauseData.eventName); if (!eventBreakpoint) return false; this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true}); var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, { className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, title: WI.UIString("%s Fired").format(pauseData.eventName), }); this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement); var eventBreakpointRow = new WI.DetailsSectionRow; eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element); this._pauseReasonGroup.rows = [eventBreakpointRow]; return true; case WI.DebuggerManager.PauseReason.Assertion: // FIXME: We should include the assertion condition string. console.assert(pauseData, "Expected data with an assertion, but found none."); if (pauseData && pauseData.message) { this._pauseReasonTextRow.text = WI.UIString("Assertion with message: %s").format(pauseData.message); return true; } this._pauseReasonTextRow.text = WI.UIString("Assertion Failed"); this._pauseReasonGroup.rows = [this._pauseReasonTextRow]; return true; case WI.DebuggerManager.PauseReason.Breakpoint: console.assert(pauseData, "Expected breakpoint identifier, but found none."); if (pauseData && pauseData.breakpointId) { this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true}); this._pauseReasonTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this); let breakpoint = WI.debuggerManager.breakpointForIdentifier(pauseData.breakpointId); let breakpointTreeElement = new WI.BreakpointTreeElement(breakpoint, { className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, title: WI.UIString("Triggered Breakpoint"), }); let breakpointDetailsSection = new WI.DetailsSectionRow; this._pauseReasonTreeOutline.appendChild(breakpointTreeElement); breakpointDetailsSection.element.appendChild(this._pauseReasonTreeOutline.element); this._pauseReasonGroup.rows = [breakpointDetailsSection]; return true; } break; case WI.DebuggerManager.PauseReason.CSPViolation: console.assert(pauseData, "Expected data with a CSP Violation, but found none."); if (pauseData) { // COMPATIBILITY (iOS 8): 'directive' was 'directiveText'. this._pauseReasonTextRow.text = WI.UIString("Content Security Policy violation of directive: %s").format(pauseData.directive || pauseData.directiveText); this._pauseReasonGroup.rows = [this._pauseReasonTextRow]; return true; } break; case WI.DebuggerManager.PauseReason.DebuggerStatement: this._pauseReasonTextRow.text = WI.UIString("Debugger Statement"); this._pauseReasonGroup.rows = [this._pauseReasonTextRow]; return true; case WI.DebuggerManager.PauseReason.DOM: console.assert(WI.domDebuggerManager.supported); console.assert(pauseData, "Expected DOM breakpoint data, but found none."); if (pauseData && pauseData.nodeId) { let domNode = WI.domManager.nodeForId(pauseData.nodeId); let domBreakpoints = WI.domDebuggerManager.domBreakpointsForNode(domNode); let domBreakpoint; for (let breakpoint of domBreakpoints) { if (breakpoint.type === pauseData.type) { domBreakpoint = breakpoint; break; } } if (!domBreakpoint) return; this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true}); let type = WI.DOMBreakpointTreeElement.displayNameForType(domBreakpoint.type); let domBreakpointTreeElement = new WI.DOMBreakpointTreeElement(domBreakpoint, { className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, title: type, }); let domBreakpointRow = new WI.DetailsSectionRow; this._pauseReasonTreeOutline.appendChild(domBreakpointTreeElement); domBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element); let ownerElementRow = new WI.DetailsSectionSimpleRow(WI.UIString("Element"), WI.linkifyNodeReference(domNode)); this._pauseReasonGroup.rows = [domBreakpointRow, ownerElementRow]; if (domBreakpoint.type !== WI.DOMBreakpoint.Type.SubtreeModified) return true; console.assert(pauseData.targetNode); let remoteObject = WI.RemoteObject.fromPayload(pauseData.targetNode, target); remoteObject.pushNodeToFrontend((nodeId) => { if (!nodeId) return; let node = WI.domManager.nodeForId(nodeId); console.assert(node, "Missing node for id.", nodeId); if (!node) return; let fragment = document.createDocumentFragment(); let description = pauseData.insertion ? WI.UIString("Child added to ") : WI.UIString("Removed descendant "); fragment.append(description, WI.linkifyNodeReference(node)); let targetDescriptionRow = new WI.DetailsSectionSimpleRow(WI.UIString("Details"), fragment); targetDescriptionRow.element.classList.add("target-description"); this._pauseReasonGroup.rows = this._pauseReasonGroup.rows.concat(targetDescriptionRow); }); return true; } break; case WI.DebuggerManager.PauseReason.EventListener: console.assert(pauseData, "Expected data with an event listener, but found none."); if (!pauseData) return false; var eventBreakpoint = null; if (pauseData.eventListenerId) eventBreakpoint = WI.domManager.breakpointForEventListenerId(pauseData.eventListenerId); if (!eventBreakpoint) eventBreakpoint = WI.domDebuggerManager.eventBreakpointForTypeAndEventName(WI.EventBreakpoint.Type.Listener, pauseData.eventName); console.assert(eventBreakpoint, "Expected Event Listener breakpoint for event name.", pauseData.eventName); if (!eventBreakpoint) return false; this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true}); var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, { className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, title: WI.UIString("\u201C%s\u201D Event Fired").format(pauseData.eventName), }); this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement); var eventBreakpointRow = new WI.DetailsSectionRow; eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element); var rows = [eventBreakpointRow]; var eventListener = eventBreakpoint.eventListener; if (eventListener) { console.assert(eventListener.eventListenerId === pauseData.eventListenerId); let value = null; if (eventListener.onWindow) value = WI.unlocalizedString("window"); else if (eventListener.node) value = WI.linkifyNodeReference(eventListener.node); if (value) rows.push(new WI.DetailsSectionSimpleRow(WI.UIString("Target"), value)); } this._pauseReasonGroup.rows = rows; return true; case WI.DebuggerManager.PauseReason.Exception: console.assert(pauseData, "Expected data with an exception, but found none."); if (pauseData) { // FIXME: We should improve the appearance of thrown objects. This works well for exception strings. var data = WI.RemoteObject.fromPayload(pauseData, target); this._pauseReasonTextRow.text = WI.UIString("Exception with thrown value: %s").format(data.description); this._pauseReasonGroup.rows = [this._pauseReasonTextRow]; return true; } break; case WI.DebuggerManager.PauseReason.PauseOnNextStatement: this._pauseReasonTextRow.text = WI.UIString("Immediate Pause Requested"); this._pauseReasonGroup.rows = [this._pauseReasonTextRow]; return true; case WI.DebuggerManager.PauseReason.Timer: console.assert(pauseData, "Expected data with a timer, but found none."); if (!pauseData) return false; var eventBreakpoint = WI.domDebuggerManager.eventBreakpointForTypeAndEventName(WI.EventBreakpoint.Type.Timer, pauseData.eventName); console.assert(eventBreakpoint, "Expected Timer breakpoint for event name.", pauseData.eventName); if (!eventBreakpoint) return false; this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true}); var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, { className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, title: WI.UIString("%s Fired").format(pauseData.eventName), }); this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement); var eventBreakpointRow = new WI.DetailsSectionRow; eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element); this._pauseReasonGroup.rows = [eventBreakpointRow]; return true; case WI.DebuggerManager.PauseReason.Fetch: case WI.DebuggerManager.PauseReason.XHR: console.assert(WI.domDebuggerManager.supported); console.assert(pauseData, "Expected URL breakpoint data, but found none."); if (pauseData) { if (pauseData.breakpointURL) { let urlBreakpoint = WI.domDebuggerManager.urlBreakpointForURL(pauseData.breakpointURL); console.assert(urlBreakpoint, "Expected URL breakpoint for URL.", pauseData.breakpointURL); this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true}); let urlBreakpointTreeElement = new WI.URLBreakpointTreeElement(urlBreakpoint, { className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, title: WI.DOMDebuggerManager.supportsURLBreakpoints() ? WI.UIString("Triggered URL Breakpoint") : WI.UIString("Triggered XHR Breakpoint"), }); let urlBreakpointRow = new WI.DetailsSectionRow; this._pauseReasonTreeOutline.appendChild(urlBreakpointTreeElement); urlBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element); this._pauseReasonTextRow.text = pauseData.url; this._pauseReasonGroup.rows = [urlBreakpointRow, this._pauseReasonTextRow]; } else { console.assert(pauseData.breakpointURL === "", "Should be the All Requests breakpoint which has an empty URL"); this._pauseReasonTextRow.text = WI.UIString("Requesting: %s").format(pauseData.url); this._pauseReasonGroup.rows = [this._pauseReasonTextRow]; } this._pauseReasonTextRow.element.title = pauseData.url; return true; } break; case WI.DebuggerManager.PauseReason.Other: console.error("Paused for unknown reason. We should always have a reason."); break; } return false; } _updatePauseReasonGotoArrow() { this._pauseReasonLinkContainerElement.removeChildren(); var activeCallFrame = WI.debuggerManager.activeCallFrame; if (!activeCallFrame) return; var sourceCodeLocation = activeCallFrame.sourceCodeLocation; if (!sourceCodeLocation) return; const options = { useGoToArrowButton: true, ignoreNetworkTab: true, ignoreSearchTab: true, }; let linkElement = WI.createSourceCodeLocationLink(sourceCodeLocation, options); this._pauseReasonLinkContainerElement.appendChild(linkElement); } _addIssue(issueMessage) { let issueTreeElement = this._scriptsContentTreeOutline.findTreeElement(issueMessage); if (issueTreeElement) return issueTreeElement; let parentTreeElement = this._addTreeElementForSourceCodeToTreeOutline(issueMessage.sourceCodeLocation.sourceCode, this._scriptsContentTreeOutline); if (!parentTreeElement) return null; issueTreeElement = new WI.IssueTreeElement(issueMessage); parentTreeElement.insertChild(issueTreeElement, insertionIndexForObjectInListSortedByFunction(issueTreeElement, parentTreeElement.children, this._compareBreakpointTreeElements)); if (parentTreeElement.children.length === 1) parentTreeElement.expand(); return issueTreeElement; } _handleDOMBreakpointDOMNodeChanged(event) { let breakpoint = event.target; if (breakpoint.domNodeIdentifier) this._addBreakpoint(breakpoint); else this._removeBreakpoint(breakpoint); } _handleIssueAdded(event) { var issue = event.data.issue; // We only want to show issues originating from JavaScript source code. if (!issue.sourceCodeLocation || !issue.sourceCodeLocation.sourceCode || (issue.source !== "javascript" && issue.source !== "console-api")) return; this._addIssue(issue); } _handleIssuesCleared(event) { let currentTreeElement = this._scriptsContentTreeOutline.children[0]; let issueTreeElements = []; while (currentTreeElement && !currentTreeElement.root) { if (currentTreeElement instanceof WI.IssueTreeElement) issueTreeElements.push(currentTreeElement); currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true); } issueTreeElements.forEach((treeElement) => treeElement.parent.removeChild(treeElement)); } _handleBreakpointElementAddedOrRemoved(event) { let treeElement = event.data.element; let setting = null; if (treeElement.breakpoint === WI.debuggerManager.assertionFailuresBreakpoint) setting = WI.settings.showAssertionFailuresBreakpoint; else if (treeElement.representedObject === WI.domDebuggerManager.allRequestsBreakpoint) setting = WI.settings.showAllRequestsBreakpoint; if (setting) setting.value = !!treeElement.parent; } _handleCreateBreakpointMouseDown(event) { if (this._ignoreCreateBreakpointMouseDown) return; this._ignoreCreateBreakpointMouseDown = true; let contextMenu = WI.ContextMenu.createFromEvent(event); contextMenu.addBeforeShowCallback(() => { this._ignoreCreateBreakpointMouseDown = false; }); // COMPATIBILITY (iOS 10): DebuggerAgent.setPauseOnAssertions did not exist yet. if (InspectorBackend.domains.Debugger.setPauseOnAssertions) { let assertionFailuresBreakpointShown = WI.settings.showAssertionFailuresBreakpoint.value; contextMenu.appendCheckboxItem(WI.repeatedUIString.assertionFailures(), () => { if (assertionFailuresBreakpointShown) WI.debuggerManager.removeBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint); else { WI.debuggerManager.assertionFailuresBreakpoint.disabled = false; WI.debuggerManager.addBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint); } }, assertionFailuresBreakpointShown); } if (WI.domDebuggerManager.supported) { contextMenu.appendSeparator(); contextMenu.appendItem(WI.UIString("Event Breakpoint\u2026"), () => { let popover = new WI.EventBreakpointPopover(this); popover.show(this._createBreakpointButton.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]); }); contextMenu.appendSeparator(); let allRequestsBreakpointShown = WI.settings.showAllRequestsBreakpoint.value; contextMenu.appendCheckboxItem(WI.repeatedUIString.allRequests(), () => { if (allRequestsBreakpointShown) WI.domDebuggerManager.removeURLBreakpoint(WI.domDebuggerManager.allRequestsBreakpoint); else { WI.domDebuggerManager.allRequestsBreakpoint.disabled = false; WI.domDebuggerManager.addURLBreakpoint(WI.domDebuggerManager.allRequestsBreakpoint); } }, allRequestsBreakpointShown); contextMenu.appendItem(WI.DOMDebuggerManager.supportsURLBreakpoints() ? WI.UIString("URL Breakpoint\u2026") : WI.UIString("XHR Breakpoint\u2026"), () => { let popover = new WI.URLBreakpointPopover(this); popover.show(this._createBreakpointButton.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]); }); } contextMenu.show(); } }; WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName = "paused"; WI.DebuggerSidebarPanel.ExceptionIconStyleClassName = "breakpoint-exception-icon"; WI.DebuggerSidebarPanel.AssertionIconStyleClassName = "breakpoint-assertion-icon"; WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName = "breakpoint-paused-icon"; WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey = "debugger-sidebar-panel-all-exceptions-breakpoint"; WI.DebuggerSidebarPanel.SelectedUncaughtExceptionsCookieKey = "debugger-sidebar-panel-uncaught-exceptions-breakpoint"; WI.DebuggerSidebarPanel.SelectedAssertionFailuresCookieKey = "debugger-sidebar-panel-assertion-failures-breakpoint"; WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey = "debugger-sidebar-panel-all-requests-breakpoint";