| /* |
| * 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. |
| */ |
| |
| WebInspector.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WebInspector.NavigationSidebarPanel |
| { |
| constructor(contentBrowser) |
| { |
| super("debugger", WebInspector.UIString("Debugger"), true); |
| |
| this.contentBrowser = contentBrowser; |
| |
| WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this); |
| WebInspector.Frame.addEventListener(WebInspector.Frame.Event.ResourceWasAdded, this._resourceAdded, this); |
| |
| WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this); |
| WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.CallFramesDidChange, this._debuggerCallFramesDidChange, this); |
| WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this); |
| WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this); |
| WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this); |
| WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ScriptsCleared, this._scriptsCleared, this); |
| WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerDidPause, this); |
| WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerDidResume, this); |
| WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange, this._debuggerActiveCallFrameDidChange, this); |
| WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.WaitingToPause, this._debuggerWaitingToPause, this); |
| |
| this._navigationBar = new WebInspector.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 = WebInspector.UIString("Enable all breakpoints (%s)").format(WebInspector.toggleBreakpointsKeyboardShortcut.displayName); |
| var altToolTip = WebInspector.UIString("Disable all breakpoints (%s)").format(WebInspector.toggleBreakpointsKeyboardShortcut.displayName); |
| |
| this._debuggerBreakpointsButtonItem = new WebInspector.ActivateButtonNavigationItem("debugger-breakpoints", toolTip, altToolTip, breakpointsImage.src, breakpointsImage.width, breakpointsImage.height); |
| this._debuggerBreakpointsButtonItem.activated = WebInspector.debuggerManager.breakpointsEnabled; |
| this._debuggerBreakpointsButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, WebInspector.debuggerToggleBreakpoints, this); |
| this._navigationBar.addNavigationItem(this._debuggerBreakpointsButtonItem); |
| |
| toolTip = WebInspector.UIString("Pause script execution (%s or %s)").format(WebInspector.pauseOrResumeKeyboardShortcut.displayName, WebInspector.pauseOrResumeAlternateKeyboardShortcut.displayName); |
| altToolTip = WebInspector.UIString("Continue script execution (%s or %s)").format(WebInspector.pauseOrResumeKeyboardShortcut.displayName, WebInspector.pauseOrResumeAlternateKeyboardShortcut.displayName); |
| |
| this._debuggerPauseResumeButtonItem = new WebInspector.ToggleButtonNavigationItem("debugger-pause-resume", toolTip, altToolTip, pauseImage.src, resumeImage.src, pauseImage.width, pauseImage.height); |
| this._debuggerPauseResumeButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, WebInspector.debuggerPauseResumeToggle, this); |
| this._navigationBar.addNavigationItem(this._debuggerPauseResumeButtonItem); |
| |
| this._debuggerStepOverButtonItem = new WebInspector.ButtonNavigationItem("debugger-step-over", WebInspector.UIString("Step over (%s or %s)").format(WebInspector.stepOverKeyboardShortcut.displayName, WebInspector.stepOverAlternateKeyboardShortcut.displayName), stepOverImage.src, stepOverImage.width, stepOverImage.height); |
| this._debuggerStepOverButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, WebInspector.debuggerStepOver, this); |
| this._debuggerStepOverButtonItem.enabled = false; |
| this._navigationBar.addNavigationItem(this._debuggerStepOverButtonItem); |
| |
| this._debuggerStepIntoButtonItem = new WebInspector.ButtonNavigationItem("debugger-step-into", WebInspector.UIString("Step into (%s or %s)").format(WebInspector.stepIntoKeyboardShortcut.displayName, WebInspector.stepIntoAlternateKeyboardShortcut.displayName), stepIntoImage.src, stepIntoImage.width, stepIntoImage.height); |
| this._debuggerStepIntoButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, WebInspector.debuggerStepInto, this); |
| this._debuggerStepIntoButtonItem.enabled = false; |
| this._navigationBar.addNavigationItem(this._debuggerStepIntoButtonItem); |
| |
| this._debuggerStepOutButtonItem = new WebInspector.ButtonNavigationItem("debugger-step-out", WebInspector.UIString("Step out (%s or %s)").format(WebInspector.stepOutKeyboardShortcut.displayName, WebInspector.stepOutAlternateKeyboardShortcut.displayName), stepOutImage.src, stepOutImage.width, stepOutImage.height); |
| this._debuggerStepOutButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, WebInspector.debuggerStepOut, this); |
| this._debuggerStepOutButtonItem.enabled = false; |
| this._navigationBar.addNavigationItem(this._debuggerStepOutButtonItem); |
| |
| // Add this offset-sections class name so the sticky headers don't overlap the navigation bar. |
| this.element.classList.add(WebInspector.DebuggerSidebarPanel.OffsetSectionsStyleClassName); |
| |
| this._globalBreakpointsFolderTreeElement = new WebInspector.FolderTreeElement(WebInspector.UIString("Global Breakpoints"), null, WebInspector.DebuggerSidebarPanel.GlobalIconStyleClassName); |
| this._allExceptionsBreakpointTreeElement = new WebInspector.BreakpointTreeElement(WebInspector.debuggerManager.allExceptionsBreakpoint, WebInspector.DebuggerSidebarPanel.ExceptionIconStyleClassName, WebInspector.UIString("All Exceptions")); |
| this._allUncaughtExceptionsBreakpointTreeElement = new WebInspector.BreakpointTreeElement(WebInspector.debuggerManager.allUncaughtExceptionsBreakpoint, WebInspector.DebuggerSidebarPanel.ExceptionIconStyleClassName, WebInspector.UIString("All Uncaught Exceptions")); |
| |
| this.filterBar.placeholder = WebInspector.UIString("Filter Breakpoint List"); |
| |
| var showResourcesWithIssuesOnlyFilterFunction = function(treeElement) |
| { |
| // Keep issues. |
| if (treeElement instanceof WebInspector.IssueTreeElement) |
| return true; |
| |
| // Keep resources with issues. |
| if (treeElement.hasChildren) { |
| for (var child of treeElement.children) { |
| if (child instanceof WebInspector.IssueTreeElement) |
| return true; |
| } |
| } |
| return false; |
| }; |
| |
| this.filterBar.addFilterBarButton("debugger-show-resources-with-issues-only", showResourcesWithIssuesOnlyFilterFunction, true, WebInspector.UIString("Show only resources with issues."), WebInspector.UIString("Show resources with and without issues."), "Images/Errors.svg", 15, 15); |
| |
| this._breakpointsContentTreeOutline = this.contentTreeOutline; |
| this._breakpointsContentTreeOutline.addEventListener(WebInspector.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this); |
| this._breakpointsContentTreeOutline.ondelete = this._breakpointTreeOutlineDeleteTreeElement.bind(this); |
| this._breakpointsContentTreeOutline.oncontextmenu = this._breakpointTreeOutlineContextMenuTreeElement.bind(this); |
| |
| this._breakpointsContentTreeOutline.appendChild(this._globalBreakpointsFolderTreeElement); |
| this._globalBreakpointsFolderTreeElement.appendChild(this._allExceptionsBreakpointTreeElement); |
| this._globalBreakpointsFolderTreeElement.appendChild(this._allUncaughtExceptionsBreakpointTreeElement); |
| this._globalBreakpointsFolderTreeElement.expand(); |
| |
| var breakpointsRow = new WebInspector.DetailsSectionRow; |
| breakpointsRow.element.appendChild(this._breakpointsContentTreeOutline.element); |
| |
| var breakpointsGroup = new WebInspector.DetailsSectionGroup([breakpointsRow]); |
| var breakpointsSection = new WebInspector.DetailsSection("scripts", WebInspector.UIString("Scripts"), [breakpointsGroup]); |
| this.contentElement.appendChild(breakpointsSection.element); |
| |
| this._callStackContentTreeOutline = this.createContentTreeOutline(true, true); |
| this._callStackContentTreeOutline.addEventListener(WebInspector.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this); |
| |
| this._callStackRow = new WebInspector.DetailsSectionRow(WebInspector.UIString("No Call Frames")); |
| this._callStackRow.showEmptyMessage(); |
| |
| var callStackGroup = new WebInspector.DetailsSectionGroup([this._callStackRow]); |
| this._callStackSection = new WebInspector.DetailsSection("call-stack", WebInspector.UIString("Call Stack"), [callStackGroup]); |
| |
| this._pauseReasonTreeOutline = null; |
| |
| this._pauseReasonLinkContainerElement = document.createElement("span"); |
| this._pauseReasonTextRow = new WebInspector.DetailsSectionTextRow; |
| this._pauseReasonGroup = new WebInspector.DetailsSectionGroup([this._pauseReasonTextRow]); |
| this._pauseReasonSection = new WebInspector.DetailsSection("paused-reason", null, [this._pauseReasonGroup], this._pauseReasonLinkContainerElement); |
| this._pauseReasonSection.title = WebInspector.UIString("Pause Reason"); |
| |
| WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this); |
| WebInspector.IssueMessage.addEventListener(WebInspector.IssueMessage.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this); |
| WebInspector.issueManager.addEventListener(WebInspector.IssueManager.Event.IssueWasAdded, this._handleIssueAdded, this); |
| WebInspector.issueManager.addEventListener(WebInspector.IssueManager.Event.Cleared, this._handleIssuesCleared, this); |
| |
| if (WebInspector.frameResourceManager.mainFrame) |
| this._addResourcesRecursivelyForFrame(WebInspector.frameResourceManager.mainFrame); |
| |
| for (var script of WebInspector.debuggerManager.knownNonResourceScripts) |
| this._addScript(script); |
| } |
| |
| // Public |
| |
| closed() |
| { |
| super.closed(); |
| |
| WebInspector.Frame.removeEventListener(null, null, this); |
| WebInspector.debuggerManager.removeEventListener(null, null, this); |
| WebInspector.Breakpoint.removeEventListener(null, null, this); |
| WebInspector.IssueMessage.removeEventListener(null, null, this); |
| } |
| |
| get hasSelectedElement() |
| { |
| return !!this._breakpointsContentTreeOutline.selectedTreeElement |
| || !!this._callStackContentTreeOutline.selectedTreeElement |
| || (this._pauseReasonTreeOutline && !!this._pauseReasonTreeOutline.selectedTreeElement); |
| } |
| |
| showDefaultContentView() |
| { |
| var currentTreeElement = this._contentTreeOutline.children[0]; |
| while (currentTreeElement && !currentTreeElement.root) { |
| if (currentTreeElement instanceof WebInspector.ResourceTreeElement || currentTreeElement instanceof WebInspector.ScriptTreeElement) { |
| 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 WebInspector.Frame) |
| representedObject = representedObject.mainResource; |
| |
| return this.contentTreeOutline.getCachedTreeElement(representedObject); |
| } |
| |
| // Protected |
| |
| saveStateToCookie(cookie) |
| { |
| console.assert(cookie); |
| |
| var selectedTreeElement = this._breakpointsContentTreeOutline.selectedTreeElement; |
| if (!selectedTreeElement) |
| return; |
| |
| var representedObject = selectedTreeElement.representedObject; |
| |
| if (representedObject === WebInspector.debuggerManager.allExceptionsBreakpoint) { |
| cookie[WebInspector.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey] = true; |
| return; |
| } |
| |
| if (representedObject === WebInspector.debuggerManager.allUncaughtExceptionsBreakpoint) { |
| cookie[WebInspector.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey] = true; |
| return; |
| } |
| |
| super.saveStateToCookie(cookie); |
| } |
| |
| restoreStateFromCookie(cookie, relaxedMatchDelay) |
| { |
| console.assert(cookie); |
| |
| // Eagerly resolve the special breakpoints; otherwise, use the default behavior. |
| if (cookie[WebInspector.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey]) |
| this._allExceptionsBreakpointTreeElement.revealAndSelect(); |
| else if (cookie[WebInspector.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey]) |
| this._allUncaughtExceptionsBreakpointTreeElement.revealAndSelect(); |
| else |
| super.restoreStateFromCookie(cookie, relaxedMatchDelay); |
| } |
| |
| // Private |
| |
| _debuggerWaitingToPause(event) |
| { |
| this._debuggerPauseResumeButtonItem.enabled = false; |
| } |
| |
| _debuggerDidPause(event) |
| { |
| this.contentElement.insertBefore(this._callStackSection.element, this.contentElement.firstChild); |
| if (this._updatePauseReason()) |
| this.contentElement.insertBefore(this._pauseReasonSection.element, this.contentElement.firstChild); |
| |
| this._debuggerPauseResumeButtonItem.enabled = true; |
| this._debuggerPauseResumeButtonItem.toggled = true; |
| this._debuggerStepOverButtonItem.enabled = true; |
| this._debuggerStepIntoButtonItem.enabled = true; |
| |
| this.element.classList.add(WebInspector.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(WebInspector.DebuggerSidebarPanel.DebuggerPausedStyleClassName); |
| } |
| |
| _breakpointsEnabledDidChange(event) |
| { |
| this._debuggerBreakpointsButtonItem.activated = WebInspector.debuggerManager.breakpointsEnabled; |
| } |
| |
| _addBreakpoint(breakpoint) |
| { |
| var sourceCode = breakpoint.sourceCodeLocation.displaySourceCode; |
| if (!sourceCode) |
| return null; |
| |
| var parentTreeElement = this._addTreeElementForSourceCodeToContentTreeOutline(sourceCode); |
| |
| // 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; |
| |
| var breakpointTreeElement = new WebInspector.BreakpointTreeElement(breakpoint); |
| parentTreeElement.insertChild(breakpointTreeElement, insertionIndexForObjectInListSortedByFunction(breakpointTreeElement, parentTreeElement.children, this._compareDebuggerTreeElements)); |
| if (parentTreeElement.children.length === 1) |
| parentTreeElement.expand(); |
| return breakpointTreeElement; |
| } |
| |
| _addBreakpointsForSourceCode(sourceCode) |
| { |
| var breakpoints = WebInspector.debuggerManager.breakpointsForSourceCode(sourceCode); |
| for (var i = 0; i < breakpoints.length; ++i) |
| this._addBreakpoint(breakpoints[i], sourceCode); |
| } |
| |
| _addIssuesForSourceCode(sourceCode) |
| { |
| var issues = WebInspector.issueManager.issuesForSourceCode(sourceCode); |
| for (var issue of issues) |
| this._addIssue(issue); |
| } |
| |
| _addTreeElementForSourceCodeToContentTreeOutline(sourceCode) |
| { |
| var treeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(sourceCode); |
| if (!treeElement) { |
| if (sourceCode instanceof WebInspector.SourceMapResource) |
| treeElement = new WebInspector.SourceMapResourceTreeElement(sourceCode); |
| else if (sourceCode instanceof WebInspector.Resource) |
| treeElement = new WebInspector.ResourceTreeElement(sourceCode); |
| else if (sourceCode instanceof WebInspector.Script) |
| treeElement = new WebInspector.ScriptTreeElement(sourceCode); |
| } |
| |
| if (!treeElement) { |
| console.error("Unknown sourceCode instance", sourceCode); |
| return; |
| } |
| |
| if (!treeElement.parent) { |
| treeElement.hasChildren = false; |
| treeElement.expand(); |
| |
| this._breakpointsContentTreeOutline.insertChild(treeElement, insertionIndexForObjectInListSortedByFunction(treeElement, this._breakpointsContentTreeOutline.children, this._compareTopLevelTreeElements.bind(this))); |
| } |
| |
| return treeElement; |
| } |
| |
| _addResourcesRecursivelyForFrame(frame) |
| { |
| this._addResource(frame.mainResource); |
| |
| for (var resource of frame.resources) |
| this._addResource(resource); |
| |
| for (var childFrame of frame.childFrames) |
| this._addResourcesRecursivelyForFrame(childFrame); |
| } |
| |
| _resourceAdded(event) |
| { |
| this._addResource(event.data.resource); |
| } |
| |
| _addResource(resource) |
| { |
| if (![WebInspector.Resource.Type.Document, WebInspector.Resource.Type.Script].includes(resource.type)) |
| return; |
| |
| let treeElement = this._addTreeElementForSourceCodeToContentTreeOutline(resource); |
| this._addBreakpointsForSourceCode(resource); |
| this._addIssuesForSourceCode(resource); |
| |
| if (!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(); |
| } |
| |
| var resource = event.target.mainResource; |
| this._addTreeElementForSourceCodeToContentTreeOutline(resource); |
| this._addBreakpointsForSourceCode(resource); |
| this._addIssuesForSourceCode(resource); |
| } |
| |
| _scriptAdded(event) |
| { |
| this._addScript(event.data.script); |
| } |
| |
| _addScript(script) |
| { |
| // FIXME: Allow for scripts generated by eval statements to appear, but filter out JSC internals |
| // and other WebInspector internals lacking __WebInspector in the url attribute. |
| if (!script.url) |
| return; |
| |
| // Exclude inspector scripts. |
| if (script.url && script.url.startsWith("__WebInspector")) |
| return; |
| |
| // Don't add breakpoints if the script is represented by a Resource. They were |
| // already added by _resourceAdded. |
| if (script.resource) |
| return; |
| |
| let treeElement = this._addTreeElementForSourceCodeToContentTreeOutline(script); |
| this._addBreakpointsForSourceCode(script); |
| this._addIssuesForSourceCode(script); |
| |
| if (!this.contentBrowser.currentContentView) |
| this.showDefaultContentViewForTreeElement(treeElement); |
| } |
| |
| _scriptsCleared(event) |
| { |
| for (var i = this._breakpointsContentTreeOutline.children.length - 1; i >= 0; --i) { |
| var treeElement = this._breakpointsContentTreeOutline.children[i]; |
| if (!(treeElement instanceof WebInspector.ScriptTreeElement)) |
| continue; |
| |
| this._breakpointsContentTreeOutline.removeChildAtIndex(i, true, true); |
| } |
| } |
| |
| _breakpointAdded(event) |
| { |
| var breakpoint = event.data.breakpoint; |
| this._addBreakpoint(breakpoint); |
| } |
| |
| _breakpointRemoved(event) |
| { |
| var breakpoint = event.data.breakpoint; |
| |
| if (this._pauseReasonTreeOutline) { |
| var pauseReasonBreakpointTreeElement = this._pauseReasonTreeOutline.getCachedTreeElement(breakpoint); |
| if (pauseReasonBreakpointTreeElement) |
| pauseReasonBreakpointTreeElement.removeStatusImage(); |
| } |
| |
| var breakpointTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(breakpoint); |
| console.assert(breakpointTreeElement); |
| if (!breakpointTreeElement) |
| return; |
| |
| this._removeDebuggerTreeElement(breakpointTreeElement); |
| } |
| |
| _handleDebuggerObjectDisplayLocationDidChange(event) |
| { |
| var debuggerObject = event.target; |
| |
| if (event.data.oldDisplaySourceCode === debuggerObject.sourceCodeLocation.displaySourceCode) |
| return; |
| |
| var debuggerTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(debuggerObject); |
| if (!debuggerTreeElement) |
| 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. |
| |
| var wasSelected = debuggerTreeElement.selected; |
| |
| this._removeDebuggerTreeElement(debuggerTreeElement); |
| var newDebuggerTreeElement = this._addDebuggerObject(debuggerObject); |
| |
| if (newDebuggerTreeElement && wasSelected) |
| newDebuggerTreeElement.revealAndSelect(true, false, true, true); |
| } |
| |
| _removeDebuggerTreeElement(debuggerTreeElement) |
| { |
| var parentTreeElement = debuggerTreeElement.parent; |
| parentTreeElement.removeChild(debuggerTreeElement); |
| |
| console.assert(parentTreeElement.parent === this._breakpointsContentTreeOutline); |
| } |
| |
| _debuggerCallFramesDidChange() |
| { |
| this._callStackContentTreeOutline.removeChildren(); |
| |
| var callFrames = WebInspector.debuggerManager.callFrames; |
| if (!callFrames || !callFrames.length) { |
| this._callStackRow.showEmptyMessage(); |
| return; |
| } |
| |
| this._callStackRow.hideEmptyMessage(); |
| this._callStackRow.element.appendChild(this._callStackContentTreeOutline.element); |
| |
| var treeElementToSelect = null; |
| |
| var activeCallFrame = WebInspector.debuggerManager.activeCallFrame; |
| for (var i = 0; i < callFrames.length; ++i) { |
| var callFrameTreeElement = new WebInspector.CallFrameTreeElement(callFrames[i]); |
| if (callFrames[i] === activeCallFrame) |
| treeElementToSelect = callFrameTreeElement; |
| this._callStackContentTreeOutline.appendChild(callFrameTreeElement); |
| } |
| |
| if (treeElementToSelect) |
| treeElementToSelect.select(true, true); |
| } |
| |
| _debuggerActiveCallFrameDidChange() |
| { |
| var callFrames = WebInspector.debuggerManager.callFrames; |
| if (!callFrames) |
| return; |
| |
| var indexOfActiveCallFrame = callFrames.indexOf(WebInspector.debuggerManager.activeCallFrame); |
| // It is useful to turn off the step out button when there is no call frame to go through |
| // since there might be call frames in the backend that were removed when processing the call |
| // frame payload. |
| this._debuggerStepOutButtonItem.enabled = indexOfActiveCallFrame < callFrames.length - 1; |
| } |
| |
| _breakpointsBeneathTreeElement(treeElement) |
| { |
| console.assert(treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement); |
| if (!(treeElement instanceof WebInspector.ResourceTreeElement) && !(treeElement instanceof WebInspector.ScriptTreeElement)) |
| return []; |
| |
| var breakpoints = []; |
| var breakpointTreeElements = treeElement.children; |
| for (var i = 0; i < breakpointTreeElements.length; ++i) { |
| console.assert(breakpointTreeElements[i] instanceof WebInspector.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 (WebInspector.debuggerManager.isBreakpointRemovable(breakpoint)) |
| WebInspector.debuggerManager.removeBreakpoint(breakpoint); |
| } |
| } |
| |
| _toggleAllBreakpoints(breakpoints, disabled) |
| { |
| for (var i = 0; i < breakpoints.length; ++i) |
| breakpoints[i].disabled = disabled; |
| } |
| |
| _breakpointTreeOutlineDeleteTreeElement(treeElement) |
| { |
| console.assert(treeElement.selected); |
| console.assert(treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement); |
| if (!(treeElement instanceof WebInspector.ResourceTreeElement) && !(treeElement instanceof WebInspector.ScriptTreeElement)) |
| return false; |
| |
| var wasTopResourceTreeElement = 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(event, treeElement) |
| { |
| console.assert(treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement || treeElement.constructor === WebInspector.FolderTreeElement); |
| if (!(treeElement instanceof WebInspector.ResourceTreeElement) && !(treeElement instanceof WebInspector.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); |
| }; |
| |
| let contextMenu = WebInspector.ContextMenu.createFromEvent(event); |
| if (shouldDisable) |
| contextMenu.appendItem(WebInspector.UIString("Disable Breakpoints"), toggleAllResourceBreakpoints); |
| else |
| contextMenu.appendItem(WebInspector.UIString("Enable Breakpoints"), toggleAllResourceBreakpoints); |
| contextMenu.appendItem(WebInspector.UIString("Delete Breakpoints"), removeAllResourceBreakpoints); |
| } |
| |
| _treeSelectionDidChange(event) |
| { |
| function deselectCallStackContentTreeElements() |
| { |
| var selectedTreeElement = this._callStackContentTreeOutline.selectedTreeElement; |
| if (selectedTreeElement) |
| selectedTreeElement.deselect(); |
| } |
| |
| function deselectBreakpointContentTreeElements() |
| { |
| var selectedTreeElement = this._breakpointsContentTreeOutline.selectedTreeElement; |
| if (selectedTreeElement) |
| selectedTreeElement.deselect(); |
| } |
| |
| function deselectPauseReasonContentTreeElements() |
| { |
| if (!this._pauseReasonTreeOutline) |
| return; |
| |
| var selectedTreeElement = this._pauseReasonTreeOutline.selectedTreeElement; |
| if (selectedTreeElement) |
| selectedTreeElement.deselect(); |
| } |
| |
| let treeElement = event.data.selectedElement; |
| if (!treeElement) |
| return; |
| |
| if (treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement) { |
| deselectCallStackContentTreeElements.call(this); |
| deselectPauseReasonContentTreeElements.call(this); |
| WebInspector.showSourceCode(treeElement.representedObject); |
| return; |
| } |
| |
| if (treeElement instanceof WebInspector.CallFrameTreeElement) { |
| // Deselect any tree element in the breakpoint / pause reason content tree outlines to prevent two selections in the sidebar. |
| deselectBreakpointContentTreeElements.call(this); |
| deselectPauseReasonContentTreeElements.call(this); |
| |
| var callFrame = treeElement.callFrame; |
| WebInspector.debuggerManager.activeCallFrame = callFrame; |
| WebInspector.showSourceCodeLocation(callFrame.sourceCodeLocation); |
| return; |
| } |
| |
| if (treeElement instanceof WebInspector.IssueTreeElement) { |
| deselectCallStackContentTreeElements.call(this); |
| deselectPauseReasonContentTreeElements.call(this); |
| WebInspector.showSourceCodeLocation(treeElement.issueMessage.sourceCodeLocation); |
| return; |
| } |
| |
| if (!(treeElement instanceof WebInspector.BreakpointTreeElement) || treeElement.parent.constructor === WebInspector.FolderTreeElement) |
| return; |
| |
| // Deselect any other tree elements to prevent two selections in the sidebar. |
| deselectCallStackContentTreeElements.call(this); |
| |
| if (treeElement.treeOutline === this._pauseReasonTreeOutline) |
| deselectBreakpointContentTreeElements.call(this); |
| else |
| deselectPauseReasonContentTreeElements.call(this); |
| |
| var breakpoint = treeElement.breakpoint; |
| if (treeElement.treeOutline === this._pauseReasonTreeOutline) { |
| WebInspector.showSourceCodeLocation(breakpoint.sourceCodeLocation); |
| return; |
| } |
| |
| if (!treeElement.parent.representedObject) |
| return; |
| |
| console.assert(treeElement.parent.representedObject instanceof WebInspector.SourceCode); |
| if (!(treeElement.parent.representedObject instanceof WebInspector.SourceCode)) |
| return; |
| |
| WebInspector.showSourceCodeLocation(breakpoint.sourceCodeLocation); |
| } |
| |
| _compareTopLevelTreeElements(a, b) |
| { |
| if (a === this._globalBreakpointsFolderTreeElement) |
| return -1; |
| if (b === this._globalBreakpointsFolderTreeElement) |
| return 1; |
| |
| return a.mainTitle.localeCompare(b.mainTitle); |
| } |
| |
| _compareDebuggerTreeElements(a, b) |
| { |
| if (!a.debuggerObject || !b.debuggerObject) |
| return 0; |
| |
| var aLocation = a.debuggerObject.sourceCodeLocation; |
| var bLocation = b.debuggerObject.sourceCodeLocation; |
| |
| 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() |
| { |
| var pauseData = WebInspector.debuggerManager.pauseData; |
| |
| switch (WebInspector.debuggerManager.pauseReason) { |
| case WebInspector.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 = WebInspector.UIString("Assertion with message: %s").format(pauseData.message); |
| return true; |
| } |
| |
| this._pauseReasonTextRow.text = WebInspector.UIString("Assertion Failed"); |
| this._pauseReasonGroup.rows = [this._pauseReasonTextRow]; |
| return true; |
| |
| case WebInspector.DebuggerManager.PauseReason.Breakpoint: |
| console.assert(pauseData, "Expected breakpoint identifier, but found none."); |
| if (pauseData && pauseData.breakpointId) { |
| var breakpoint = WebInspector.debuggerManager.breakpointForIdentifier(pauseData.breakpointId); |
| var breakpointTreeOutline = this.createContentTreeOutline(true, true); |
| breakpointTreeOutline.addEventListener(WebInspector.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this); |
| |
| var breakpointTreeElement = new WebInspector.BreakpointTreeElement(breakpoint, WebInspector.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName, WebInspector.UIString("Triggered Breakpoint")); |
| var breakpointDetailsSection = new WebInspector.DetailsSectionRow; |
| breakpointTreeOutline.appendChild(breakpointTreeElement); |
| breakpointDetailsSection.element.appendChild(breakpointTreeOutline.element); |
| |
| this._pauseReasonGroup.rows = [breakpointDetailsSection]; |
| this._pauseReasonTreeOutline = breakpointTreeOutline; |
| return true; |
| } |
| break; |
| |
| case WebInspector.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 = WebInspector.UIString("Content Security Policy violation of directive: %s").format(pauseData.directive || pauseData.directiveText); |
| this._pauseReasonGroup.rows = [this._pauseReasonTextRow]; |
| return true; |
| } |
| break; |
| |
| case WebInspector.DebuggerManager.PauseReason.DebuggerStatement: |
| this._pauseReasonTextRow.text = WebInspector.UIString("Debugger Statement"); |
| this._pauseReasonGroup.rows = [this._pauseReasonTextRow]; |
| return true; |
| |
| case WebInspector.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 = WebInspector.RemoteObject.fromPayload(pauseData); |
| this._pauseReasonTextRow.text = WebInspector.UIString("Exception with thrown value: %s").format(data.description); |
| this._pauseReasonGroup.rows = [this._pauseReasonTextRow]; |
| return true; |
| } |
| break; |
| |
| case WebInspector.DebuggerManager.PauseReason.PauseOnNextStatement: |
| this._pauseReasonTextRow.text = WebInspector.UIString("Immediate Pause Requested"); |
| this._pauseReasonGroup.rows = [this._pauseReasonTextRow]; |
| return true; |
| |
| case WebInspector.DebuggerManager.PauseReason.Other: |
| console.error("Paused for unknown reason. We should always have a reason."); |
| break; |
| } |
| |
| return false; |
| } |
| |
| _updatePauseReasonGotoArrow() |
| { |
| this._pauseReasonLinkContainerElement.removeChildren(); |
| |
| var activeCallFrame = WebInspector.debuggerManager.activeCallFrame; |
| if (!activeCallFrame) |
| return; |
| |
| var sourceCodeLocation = activeCallFrame.sourceCodeLocation; |
| if (!sourceCodeLocation) |
| return; |
| |
| var linkElement = WebInspector.createSourceCodeLocationLink(sourceCodeLocation, false, true); |
| this._pauseReasonLinkContainerElement.appendChild(linkElement); |
| } |
| |
| _addDebuggerObject(debuggerObject) |
| { |
| if (debuggerObject instanceof WebInspector.Breakpoint) |
| return this._addBreakpoint(debuggerObject); |
| |
| if (debuggerObject instanceof WebInspector.IssueMessage) |
| return this._addIssue(debuggerObject); |
| } |
| |
| _addIssue(issueMessage) |
| { |
| var parentTreeElement = this._addTreeElementForSourceCodeToContentTreeOutline(issueMessage.sourceCodeLocation.sourceCode); |
| if (!parentTreeElement) |
| return null; |
| |
| var issueTreeElement = new WebInspector.IssueTreeElement(issueMessage); |
| |
| parentTreeElement.insertChild(issueTreeElement, insertionIndexForObjectInListSortedByFunction(issueTreeElement, parentTreeElement.children, this._compareDebuggerTreeElements)); |
| if (parentTreeElement.children.length === 1) |
| parentTreeElement.expand(); |
| |
| return issueTreeElement; |
| } |
| |
| _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) |
| { |
| var currentTreeElement = this._contentTreeOutline.children[0]; |
| var issueTreeElements = []; |
| |
| while (currentTreeElement && !currentTreeElement.root) { |
| if (currentTreeElement instanceof WebInspector.IssueTreeElement) |
| issueTreeElements.push(currentTreeElement); |
| currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true); |
| } |
| |
| for (var issueTreeElement of issueTreeElements) |
| issueTreeElement.parent.removeChild(issueTreeElement); |
| } |
| }; |
| |
| WebInspector.DebuggerSidebarPanel.OffsetSectionsStyleClassName = "offset-sections"; |
| WebInspector.DebuggerSidebarPanel.DebuggerPausedStyleClassName = "paused"; |
| WebInspector.DebuggerSidebarPanel.ExceptionIconStyleClassName = "breakpoint-exception-icon"; |
| WebInspector.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName = "breakpoint-paused-icon"; |
| WebInspector.DebuggerSidebarPanel.GlobalIconStyleClassName = "global-breakpoints-icon"; |
| |
| WebInspector.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey = "debugger-sidebar-panel-all-exceptions-breakpoint"; |
| WebInspector.DebuggerSidebarPanel.SelectedAllUncaughtExceptionsCookieKey = "debugger-sidebar-panel-all-uncaught-exceptions-breakpoint"; |